Initial release: MCPTesta v1.0.0 🧪

Community-driven testing excellence for the MCP ecosystem

MCPTesta is a comprehensive testing framework for FastMCP servers that brings
scientific rigor and enterprise-grade capabilities to MCP protocol testing.

🎯 Core Features:
• Comprehensive FastMCP server testing with advanced protocol support
• Parallel execution with intelligent dependency resolution
• Flexible CLI and YAML configuration system
• Rich reporting: console, HTML, JSON, and JUnit formats
• Advanced MCP protocol features: notifications, cancellation, progress tracking
• Production-ready Docker environment with caddy-docker-proxy integration

🧪 Advanced Testing Capabilities:
• Multi-transport support (stdio, SSE, WebSocket)
• Authentication testing (Bearer tokens, OAuth flows)
• Stress testing and performance validation
• Memory profiling and leak detection
• CI/CD integration with comprehensive reporting

🎨 Professional Assets:
• Complete logo package with lab experiment theme
• Comprehensive documentation with Diátaxis framework
• Community-focused branding and messaging
• Multi-platform favicon and social media assets

📚 Documentation:
• Getting started tutorials and comprehensive guides
• Complete CLI and YAML reference documentation
• Architecture explanations and testing strategies
• Team collaboration and security compliance guides

🚀 Ready for:
• Community contributions and external development
• Enterprise deployment and production use
• Integration with existing FastMCP workflows
• Extension and customization for specific needs

Built with modern Python practices using uv, FastMCP, and Starlight documentation.
Designed for developers who demand scientific precision in their testing tools.

Repository: https://git.supported.systems/mcp/mcptesta
Documentation: https://mcptesta.l.supported.systems
This commit is contained in:
Ryan Malloy 2025-09-20 03:20:49 -06:00
commit bea4a2e5d3
94 changed files with 42099 additions and 0 deletions

29
.env.example Normal file
View File

@ -0,0 +1,29 @@
# MCPTesta Environment Configuration
# Copy this file to .env and customize for your environment
# Project Configuration
COMPOSE_PROJECT=mcptesta
# Environment Mode (dev/prod)
ENVIRONMENT=dev
# Documentation Site Configuration
DOMAIN=mcptesta.l.supported.systems
PORT=3000
# Development Settings
ENABLE_HOT_RELOAD=true
ENABLE_DEBUG=true
# Production Settings (when ENVIRONMENT=prod)
# ENABLE_SSL=true
# NGINX_WORKERS=auto
# LOG_LEVEL=warn
# Docker Configuration
DOCKER_BUILDKIT=1
COMPOSE_DOCKER_CLI_BUILD=1
# Optional: Custom paths
# DOCS_SOURCE_PATH=./docs
# CONFIG_PATH=./config

116
.gitignore vendored Normal file
View File

@ -0,0 +1,116 @@
# Python
__pycache__/
*.py[cod]
*$py.class
*.so
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
*.manifest
*.spec
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
.hypothesis/
.pytest_cache/
# Virtual environments
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
.python-version
# Environment files (keep .env.example as template)
.env
.env.local
.env.development
.env.production
.env.*.local
# UV (keep lock file for reproducible builds)
# uv.lock
# IDEs
.vscode/
.idea/
*.swp
*.swo
*~
# OS
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
ehthumbs.db
Thumbs.db
# MCPTesta specific
test_reports/
demo_reports/
expert_test_results/
*.log
*.tmp
# Demo and development files
demo_*.py
*_demo.py
temp_*
scratch_*
# Documentation artifacts
PRIORITY_*_IMPLEMENTATION_SUMMARY.md
ENHANCED_*_SUMMARY.md
# Test configurations that may contain sensitive data
test_*.yaml
*_test.yaml
# Docker
.env.local
.env.development
.env.production
.env.*.local
# Documentation build artifacts
docs/dist/
docs/.astro/
docs/node_modules/
# Docker volumes and data
*_data/
*_cache/
*_logs/
# Container runtime files
*.pid
*.lock
docker-compose.override.yml

340
DOCKER.md Normal file
View File

@ -0,0 +1,340 @@
# MCPTesta Docker Environment
A comprehensive Docker Compose setup for MCPTesta documentation with development and production configurations.
## Quick Start
### Prerequisites
- Docker and Docker Compose installed
- Caddy external network created: `make caddy-network`
### Development Environment
```bash
# Clone and setup
git clone <repository>
cd mcptesta
# Start development environment
make up
# OR manually:
# make setup && make dev
# View logs
make logs-live
# Access the site
open http://mcptesta.l.supported.systems
```
### Production Environment
```bash
# Switch to production mode
make env-prod
# Start production environment
make prod
# Monitor production
make prod-logs
```
## Architecture
### Services
#### `docs` (Documentation Site)
- **Development**: Astro with hot reloading and volume mounts
- **Production**: Multi-stage build with nginx serving static files
- **Features**:
- Automatic HTTPS via Caddy reverse proxy
- Health checks and logging
- Security headers and content optimization
- Resource limits and monitoring
### Networks
#### `caddy` (External)
- Connects documentation to Caddy reverse proxy
- Provides automatic HTTPS and load balancing
- Must be created externally: `docker network create caddy`
#### `monitoring` (Internal)
- Service monitoring and health checks
- Log aggregation and metrics collection
#### `internal` (Build-only)
- Isolated network for build processes
- Production image building and artifact handling
### Volumes
#### Development
- `docs_dev_cache`: Astro build cache for faster rebuilds
- `dev_data`: SQLite database for development testing
- `dev_redis`: Redis cache for development
#### Production
- `docs_build`: Production build artifacts
- Persistent storage for static assets
## Configuration
### Environment Variables (.env)
```bash
# Project isolation
COMPOSE_PROJECT=mcptesta
# Environment mode
NODE_ENV=development # or 'production'
# Domain configuration
DOCS_DOMAIN=mcptesta.l.supported.systems
# Resource limits
DOCS_MEMORY_LIMIT=512m
DOCS_CPU_LIMIT=0.5
# Health check configuration
HEALTH_CHECK_INTERVAL=30s
HEALTH_CHECK_TIMEOUT=10s
HEALTH_CHECK_RETRIES=3
```
### Caddy Integration
The documentation site integrates with `caddy-docker-proxy` using labels:
```yaml
labels:
caddy: mcptesta.l.supported.systems
caddy.reverse_proxy: "{{upstreams 4321}}"
caddy.encode: gzip
caddy.header.Cache-Control: "public, max-age=31536000"
```
## Development Features
### Hot Reloading
- Source files mounted as volumes
- Astro dev server with automatic rebuilds
- LiveReload integration for instant updates
### Debugging
```bash
# Access container shell
make shell
# View real-time logs
make logs-live
# Check container health
make health
# Debug network connectivity
make network
```
### File Watching
```bash
# Enable file watcher (requires inotify-tools)
docker compose --profile watcher up -d docs-watcher
# Manual watch mode
make watch
```
## Production Features
### Security
- Read-only root filesystem
- Non-root user execution
- Security headers and content policies
- Minimal attack surface with Alpine Linux
### Performance
- Multi-stage builds for minimal image size
- Gzip/Zstd compression
- Static asset caching
- Resource limits and health monitoring
### Monitoring
```bash
# Enable production monitoring
docker compose --profile monitoring up -d
# Check logs
make prod-logs
# View metrics
docker compose exec docs-monitor wget -qO- http://localhost:9100/metrics
```
## Available Commands (Makefile)
### Development
- `make dev` - Start development environment
- `make dev-detached` - Start in background
- `make dev-logs` - Follow development logs
### Production
- `make prod` - Start production environment
- `make prod-build` - Build production images
- `make prod-logs` - Follow production logs
### Management
- `make build` - Build development images
- `make rebuild` - Rebuild without cache
- `make clean` - Stop and remove containers/volumes
- `make deep-clean` - Full cleanup including images
### Monitoring
- `make logs` - Show all logs
- `make status` - Container status
- `make health` - Health check status
- `make restart` - Restart services
### Utilities
- `make shell` - Access container shell
- `make test` - Run health tests
- `make network` - Show network info
- `make env` - Show environment config
### Environment
- `make env-dev` - Switch to development
- `make env-prod` - Switch to production
- `make setup` - Initial setup
## File Structure
```
mcptesta/
├── .env # Environment configuration
├── docker-compose.yml # Main compose file
├── docker-compose.dev.yml # Development overrides
├── docker-compose.prod.yml # Production overrides
├── Makefile # Management commands
├── DOCKER.md # This documentation
├── docs/
│ ├── Dockerfile # Multi-stage documentation build
│ ├── package.json # Node.js dependencies
│ ├── astro.config.mjs # Astro configuration
│ └── src/ # Documentation source
├── config/
│ ├── nginx-dev.conf # Development nginx config
│ └── fluent-bit.conf # Production logging config
└── scripts/ # Utility scripts
```
## Troubleshooting
### Common Issues
#### Container won't start
```bash
# Check logs
make logs
# Verify environment
make env
# Check network
make network
```
#### Caddy network missing
```bash
# Create external network
make caddy-network
# OR manually:
# docker network create caddy
```
#### Permission issues
```bash
# Fix file permissions
sudo chown -R $USER:$USER docs/
sudo chmod -R 755 docs/
```
#### Port conflicts
```bash
# Check port usage
netstat -tlnp | grep :4321
# Modify port in .env
echo "DOCS_PORT=4322" >> .env
```
### Debug Commands
```bash
# Full debug information
make debug
# Container inspection
docker compose exec docs sh -c "id && ls -la && env"
# Network connectivity
docker compose exec docs wget -qO- http://localhost:4321/
# Resource usage
docker stats $(docker compose ps -q)
```
### Health Checks
Health checks are configured for all services:
- **Interval**: 30 seconds
- **Timeout**: 10 seconds
- **Retries**: 3
- **Start Period**: 40 seconds
```bash
# Check health status
make health
# Manual health check
docker compose exec docs wget --spider -q http://localhost:4321/
```
## Integration with MCPTesta
This Docker environment is designed to work seamlessly with the MCPTesta project:
### Documentation Integration
- Astro/Starlight serves the Diátaxis-structured documentation
- Hot reloading for documentation development
- Integration with MCPTesta's existing docs/ structure
### Development Workflow
- Local MCPTesta development with live documentation
- Testing documentation changes in real-time
- Production-ready deployment pipeline
### CI/CD Integration
- Production builds for deployment
- Health checks for deployment validation
- Logging and monitoring for production environments
## Best Practices
### Development
1. Always use `make` commands for consistency
2. Check logs regularly: `make logs-live`
3. Use development environment for documentation editing
4. Test production builds before deployment
### Production
1. Use production environment for staging/production
2. Monitor resource usage and health
3. Enable logging for production troubleshooting
4. Regular backups of persistent volumes
### Security
1. Keep base images updated
2. Review security headers configuration
3. Monitor access logs
4. Use read-only filesystems in production
This Docker environment provides a robust, scalable, and secure foundation for MCPTesta documentation development and deployment.

180
Makefile Normal file
View File

@ -0,0 +1,180 @@
# MCPTesta Docker Compose Management
# Modern Makefile for managing Docker environments
.PHONY: help dev prod build clean logs status health restart shell test network caddy
# Default target
help: ## Show this help message
@echo "MCPTesta Docker Compose Commands"
@echo "================================"
@awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m<target>\033[0m\n"} /^[a-zA-Z_-]+:.*?##/ { printf " \033[36m%-15s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST)
##@ Development Commands
dev: ## Start development environment with hot reloading
@echo "🚀 Starting MCPTesta development environment..."
@docker compose up --build --remove-orphans
dev-detached: ## Start development environment in background
@echo "🚀 Starting MCPTesta development environment (detached)..."
@docker compose up -d --build --remove-orphans
dev-logs: ## Follow development logs
@docker compose logs -f docs
##@ Production Commands
prod: ## Start production environment
@echo "🏭 Starting MCPTesta production environment..."
@docker compose -f docker-compose.yml -f docker-compose.prod.yml up --build -d
prod-logs: ## Follow production logs
@docker compose -f docker-compose.yml -f docker-compose.prod.yml logs -f
prod-build: ## Build production images
@echo "🔨 Building production images..."
@docker compose -f docker-compose.yml -f docker-compose.prod.yml build
##@ Management Commands
build: ## Build development images
@echo "🔨 Building development images..."
@docker compose build
rebuild: ## Rebuild images without cache
@echo "🔨 Rebuilding images without cache..."
@docker compose build --no-cache
clean: ## Stop containers and remove volumes
@echo "🧹 Cleaning up containers and volumes..."
@docker compose down -v --remove-orphans
@docker system prune -f
deep-clean: ## Full cleanup including images and build cache
@echo "🧹 Deep cleaning - removing images and build cache..."
@docker compose down -v --remove-orphans --rmi all
@docker system prune -af --volumes
##@ Monitoring Commands
logs: ## Show all container logs
@docker compose logs --tail=100
logs-live: ## Follow all container logs
@docker compose logs -f
status: ## Show container status
@echo "📊 Container Status:"
@docker compose ps
@echo ""
@echo "🌐 Network Status:"
@docker network ls | grep mcptesta
health: ## Check container health
@echo "🏥 Health Check Status:"
@docker compose ps --format "table {{.Name}}\t{{.Status}}\t{{.Health}}"
restart: ## Restart all services
@echo "🔄 Restarting services..."
@docker compose restart
##@ Utility Commands
shell: ## Access docs container shell
@docker compose exec docs sh
shell-root: ## Access docs container as root
@docker compose exec --user root docs sh
test: ## Run container tests
@echo "🧪 Running container health tests..."
@docker compose exec docs wget --spider -q http://localhost:4321/ && echo "✅ Docs container healthy" || echo "❌ Docs container unhealthy"
##@ Network Commands
network: ## Show network information
@echo "🌐 Docker Networks:"
@docker network ls | grep -E "(caddy|mcptesta)"
@echo ""
@echo "🔗 Container Network Details:"
@docker compose exec docs ip route show
caddy-network: ## Create caddy external network if it doesn't exist
@echo "🌐 Ensuring caddy network exists..."
@docker network create caddy 2>/dev/null || echo " Caddy network already exists"
##@ Environment Commands
env: ## Show current environment configuration
@echo "⚙️ Current Environment Configuration:"
@echo "COMPOSE_PROJECT: $(shell grep COMPOSE_PROJECT .env | cut -d= -f2)"
@echo "NODE_ENV: $(shell grep NODE_ENV .env | cut -d= -f2)"
@echo "DOCS_DOMAIN: $(shell grep DOCS_DOMAIN .env | cut -d= -f2)"
@echo ""
@echo "📄 Full .env file:"
@cat .env
env-dev: ## Switch to development environment
@echo "🔧 Switching to development environment..."
@sed -i 's/NODE_ENV=.*/NODE_ENV=development/' .env
@echo "✅ Environment set to development"
env-prod: ## Switch to production environment
@echo "🔧 Switching to production environment..."
@sed -i 's/NODE_ENV=.*/NODE_ENV=production/' .env
@echo "✅ Environment set to production"
##@ Quick Start Commands
validate: ## Validate complete Docker setup
@echo "🔍 Validating MCPTesta Docker setup..."
@./scripts/validate-setup.sh
setup: caddy-network validate ## Initial setup - create networks and prepare environment
@echo "🎯 Setting up MCPTesta Docker environment..."
@echo "✅ Setup complete! Run 'make dev' to start development"
up: setup dev ## Complete setup and start development environment
stop: ## Stop all containers
@echo "⏹️ Stopping all containers..."
@docker compose down
##@ Documentation Commands
docs-build: ## Build documentation only
@echo "📚 Building documentation..."
@docker compose --profile build up docs-builder
docs-preview: ## Preview production build locally
@echo "👀 Previewing production documentation build..."
@docker compose exec docs npm run preview
##@ Debugging Commands
debug: ## Show debugging information
@echo "🔍 MCPTesta Docker Debug Information"
@echo "===================================="
@echo ""
@echo "📦 Docker Version:"
@docker version --format '{{.Server.Version}}'
@echo ""
@echo "🐳 Docker Compose Version:"
@docker compose version --short
@echo ""
@echo "⚙️ Environment:"
@make env
@echo ""
@echo "📊 Container Status:"
@make status
@echo ""
@echo "🌐 Network Status:"
@make network
# Development helpers
watch: ## Watch for changes and rebuild (requires inotify-tools)
@echo "👀 Watching for changes..."
@while inotifywait -r -e modify,create,delete ./docs/src; do \
echo "🔄 Changes detected, rebuilding..."; \
docker compose restart docs; \
done

330
README-DOCKER.md Normal file
View File

@ -0,0 +1,330 @@
# MCPTesta Docker Environment
🐳 **Comprehensive Docker Compose setup for MCPTesta documentation with development and production configurations.**
## 🚀 Quick Start
### Prerequisites
- Docker and Docker Compose installed
- Make utility
- Internet connection for downloading base images
### One-Command Setup
```bash
make up
```
This will:
1. Validate your environment
2. Create required networks
3. Build and start the documentation site
4. Make it available at `http://mcptesta.l.supported.systems`
## 📋 Available Commands
### 🔧 Quick Start
```bash
make validate # Validate setup
make setup # Initial setup + validation
make up # Complete setup and start dev environment
```
### 🔨 Development
```bash
make dev # Start development with hot reloading
make dev-detached # Start development in background
make dev-logs # Follow development logs
```
### 🏭 Production
```bash
make env-prod # Switch to production mode
make prod # Start production environment
make prod-logs # Follow production logs
```
### 📊 Monitoring
```bash
make status # Show container status
make health # Check health status
make logs # Show all logs
make logs-live # Follow all logs in real-time
```
### 🛠️ Management
```bash
make build # Build images
make rebuild # Rebuild without cache
make restart # Restart all services
make clean # Stop and cleanup
make deep-clean # Full cleanup including images
```
### 🐛 Debugging
```bash
make shell # Access container shell
make debug # Show comprehensive debug info
make network # Show network information
```
## 🏗️ Architecture
### Services
#### `docs` - Documentation Site
- **Development**: Astro with hot reloading
- **Production**: Static build served by nginx
- **Domain**: `mcptesta.l.supported.systems`
- **Port**: 4321
- **Features**: HTTPS, caching, compression, security headers
### Networks
#### `caddy` (External)
- Reverse proxy network for automatic HTTPS
- Shared with other projects using caddy-docker-proxy
#### `monitoring` (Internal)
- Service health monitoring
- Metrics collection
### Volumes
#### Development
- Live code mounting for hot reloading
- Separate node_modules volume
- Build cache for performance
#### Production
- Static build artifacts
- Optimized for performance and security
## ⚙️ Configuration
### Environment Variables (.env)
```bash
# Core configuration
COMPOSE_PROJECT=mcptesta
NODE_ENV=development
DOCS_DOMAIN=mcptesta.l.supported.systems
# Resource limits
DOCS_MEMORY_LIMIT=512m
DOCS_CPU_LIMIT=0.5
# Health checks
HEALTH_CHECK_INTERVAL=30s
HEALTH_CHECK_TIMEOUT=10s
HEALTH_CHECK_RETRIES=3
```
### Environment Modes
#### Development Mode
- Hot reloading enabled
- Volume mounts for live editing
- Verbose logging
- Relaxed security settings
- Debug ports exposed
#### Production Mode
- Static build optimization
- Read-only filesystem
- Enhanced security headers
- Resource limits enforced
- Monitoring enabled
## 🔄 Switching Between Modes
```bash
# Switch to development
make env-dev
make dev
# Switch to production
make env-prod
make prod
```
## 🎯 Integration Features
### Caddy Integration
Automatic reverse proxy with:
- HTTPS certificates via Let's Encrypt
- Load balancing
- Compression (gzip/zstd)
- Security headers
- Caching policies
### Hot Reloading
Development environment supports:
- Astro dev server with instant rebuilds
- File watching with automatic restarts
- LiveReload integration
- Source map support
### Security
Production environment includes:
- Non-root user execution
- Read-only root filesystem
- Security headers (HSTS, CSP, etc.)
- Minimal attack surface
- Regular security updates
## 📁 File Structure
```
mcptesta/
├── .env # Environment configuration
├── docker-compose.yml # Main compose configuration
├── docker-compose.dev.yml # Development overrides
├── docker-compose.prod.yml # Production overrides
├── Makefile # Management commands
├── DOCKER.md # Detailed documentation
├── README-DOCKER.md # This quick reference
├── docs/
│ ├── Dockerfile # Multi-stage documentation build
│ ├── .dockerignore # Build optimization
│ ├── package.json # Node.js dependencies
│ └── src/ # Documentation source
├── config/
│ ├── nginx-dev.conf # Development proxy config
│ └── fluent-bit.conf # Production logging
└── scripts/
├── health-check.sh # Container health validation
├── start-docs.sh # Container startup script
└── validate-setup.sh # Environment validation
```
## 🧪 Testing & Validation
### Health Checks
Comprehensive health monitoring:
```bash
# Manual health check
make health
# Container-level health check
docker compose exec docs /app/scripts/health-check.sh
```
### Validation
Pre-flight validation:
```bash
# Validate complete setup
make validate
# Check specific components
docker compose config # Validate compose files
./scripts/validate-setup.sh # Full environment check
```
## 🚨 Troubleshooting
### Common Issues
#### "Caddy network not found"
```bash
make caddy-network
```
#### "Port 4321 already in use"
```bash
# Check what's using the port
netstat -tlnp | grep :4321
# Or change port in .env
echo "DOCS_PORT=4322" >> .env
```
#### "Permission denied" errors
```bash
# Fix ownership
sudo chown -R $USER:$USER docs/
# Fix permissions
chmod +x scripts/*.sh
```
#### Container won't start
```bash
# Check logs
make logs
# Debug container
make shell
# Full debug info
make debug
```
### Debug Commands
```bash
# Container status
make status
# Network connectivity
make network
# Resource usage
docker stats $(docker compose ps -q)
# Validate configuration
docker compose config
# Test health endpoint
curl -f http://mcptesta.l.supported.systems/ || echo "Site not accessible"
```
## 🎨 Customization
### Custom Domain
```bash
# Change domain in .env
DOCS_DOMAIN=mydocs.example.com
# Restart services
make restart
```
### Resource Limits
```bash
# Modify .env
DOCS_MEMORY_LIMIT=1g
DOCS_CPU_LIMIT=1.0
# Apply changes
make restart
```
### Additional Services
Add to `docker-compose.yml`:
```yaml
my-service:
image: my-image:latest
networks:
- caddy
labels:
caddy: myservice.l.supported.systems
caddy.reverse_proxy: "{{upstreams 8080}}"
```
## 🔗 Related Documentation
- [DOCKER.md](./DOCKER.md) - Comprehensive Docker documentation
- [docs/README.md](./docs/README.md) - Documentation site details
- [MCPTesta README](./README.md) - Main project documentation
## 💡 Pro Tips
1. **Use make commands** - They handle complexity and provide consistent behavior
2. **Check logs regularly** - `make logs-live` shows real-time activity
3. **Validate before changes** - `make validate` catches issues early
4. **Use development mode** - Hot reloading makes documentation editing fast
5. **Monitor resources** - `make debug` shows comprehensive system info
6. **Keep it clean** - `make clean` prevents disk space issues
---
**Happy Dockerizing! 🐳**

390
README.md Normal file
View File

@ -0,0 +1,390 @@
<div align="center">
<img src="assets/logo/web/mcptesta-logo.svg" alt="MCPTesta - Lab Experiment in Progress" width="128" height="128">
# MCPTesta
**Community-driven testing excellence for the MCP ecosystem**
*Advanced testing framework for FastMCP servers with parallel execution, YAML configurations, and comprehensive MCP protocol support.*
</div>
[![Python 3.11+](https://img.shields.io/badge/python-3.11+-blue.svg)](https://www.python.org/downloads/)
[![FastMCP](https://img.shields.io/badge/FastMCP-0.9.0+-green.svg)](https://github.com/jlowin/fastmcp)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
## ✨ Features
### 🎯 **Core Testing Capabilities**
- **CLI & YAML Configuration** - Flexible test definition with command-line parameters or comprehensive YAML files
- **Parallel Execution** - Intelligent workload distribution with dependency resolution
- **Multiple Transports** - Support for stdio, SSE, and WebSocket transports
- **Advanced Reporting** - Console, HTML, JSON, and JUnit output formats
### 🚀 **Advanced MCP Protocol Support**
- **📢 Notification Testing** - Test resource/tool/prompt list change notifications
- **🔄 Progress Monitoring** - Real-time progress reporting for long-running operations
- **❌ Cancellation Support** - Request cancellation and cleanup testing
- **📊 Sampling Mechanisms** - Configurable request sampling and throttling
- **🔐 Authentication** - Bearer token and OAuth authentication testing
### ⚡ **Performance & Reliability**
- **Dependency Resolution** - Automatic test dependency management and execution ordering
- **Stress Testing** - Concurrent operation testing and load simulation
- **Memory Profiling** - Built-in memory usage monitoring and leak detection
- **Error Handling** - Comprehensive error scenario testing and validation
## 🚀 Quick Start
### Installation
```bash
# Using uv (recommended)
uv sync
uv run mcptesta --version
# Using pip
pip install -e .
mcptesta --version
```
### Basic CLI Usage
```bash
# Test a FastMCP server with CLI parameters
mcptesta test --server "python -m my_fastmcp_server" --parallel 4 --output ./results
# Test with advanced features
mcptesta test \
--server "uvx my-mcp-server" \
--transport stdio \
--test-notifications \
--test-cancellation \
--test-progress \
--stress-test
# Validate server connection
mcptesta validate --server "python -m my_fastmcp_server"
# Ping server for connectivity testing
mcptesta ping --server "python -m my_fastmcp_server" --count 10
```
### YAML Configuration
```bash
# Run comprehensive tests from YAML configuration
mcptesta yaml examples/comprehensive_test.yaml
# Override configuration parameters
mcptesta yaml config.yaml --parallel 8 --output ./custom_results
# Dry run to validate configuration
mcptesta yaml config.yaml --dry-run
# List all tests that would be executed
mcptesta yaml config.yaml --list-tests
```
### Generate Configuration Templates
```bash
# Generate basic configuration template
mcptesta generate-config basic ./my_test_config.yaml
# Generate advanced configuration with all features
mcptesta generate-config comprehensive ./advanced_config.yaml
```
## 📋 YAML Configuration Format
MCPTesta uses a comprehensive YAML format for defining complex test scenarios:
```yaml
# Global configuration
config:
parallel_workers: 4
output_format: "html"
features:
test_notifications: true
test_cancellation: true
test_progress: true
test_sampling: true
# Server configurations
servers:
- name: "my_server"
command: "python -m my_fastmcp_server"
transport: "stdio"
timeout: 30
# Test suites with dependency management
test_suites:
- name: "Basic Tests"
parallel: true
tests:
- name: "ping_test"
test_type: "ping"
timeout: 5
- name: "echo_test"
test_type: "tool_call"
target: "echo"
parameters:
message: "Hello World"
expected:
message: "Hello World"
depends_on: ["ping_test"]
```
See [examples/comprehensive_test.yaml](examples/comprehensive_test.yaml) for a complete configuration example.
## 🧪 Test Types
### Core Test Types
| Test Type | Description | Parameters |
|-----------|-------------|------------|
| `ping` | Connectivity testing | `timeout` |
| `tool_call` | Tool execution testing | `target`, `parameters`, `expected` |
| `resource_read` | Resource access testing | `target`, `expected` |
| `prompt_get` | Prompt generation testing | `target`, `arguments`, `expected` |
### Advanced Test Features
```yaml
tests:
- name: "advanced_test"
test_type: "tool_call"
target: "my_tool"
# Progress monitoring
enable_progress: true
# Cancellation support
enable_cancellation: true
# Sampling configuration
enable_sampling: true
sampling_rate: 0.8
# Retry logic
retry_count: 3
# Dependencies
depends_on: ["setup_test"]
```
## 🎯 Advanced Protocol Features
### Notification Testing
Test MCP notification system for list changes:
```yaml
tests:
- name: "notification_test"
test_type: "notification"
target: "resources_list_changed"
timeout: 30
```
### Progress Reporting
Monitor long-running operations with real-time progress:
```yaml
tests:
- name: "progress_test"
test_type: "tool_call"
target: "long_task"
enable_progress: true
timeout: 60
```
### Cancellation Testing
Test request cancellation and cleanup:
```yaml
tests:
- name: "cancellation_test"
test_type: "tool_call"
target: "slow_task"
enable_cancellation: true
timeout: 5 # Will trigger cancellation
```
### Sampling Mechanisms
Configure request sampling and throttling:
```yaml
tests:
- name: "sampling_test"
test_type: "tool_call"
target: "echo"
enable_sampling: true
sampling_rate: 0.5 # 50% sampling rate
```
## 🔧 Advanced Configuration
### Parallel Execution
MCPTesta automatically resolves test dependencies and creates optimal execution plans:
```yaml
config:
parallel_workers: 8
max_concurrent_operations: 20
test_suites:
- name: "Parallel Suite"
parallel: true # Enable parallel execution within suite
tests:
- name: "test_a"
# Runs immediately
- name: "test_b"
depends_on: ["test_a"] # Runs after test_a
- name: "test_c"
# Runs in parallel with test_a
```
### Multiple Servers
Test across multiple server instances:
```yaml
servers:
- name: "server_1"
command: "python -m server1"
transport: "stdio"
- name: "server_2"
command: "uvx server2 --port 8080"
transport: "sse"
- name: "server_3"
command: "ws://localhost:8081/mcp"
transport: "ws"
```
### Environment Variables
Use variable substitution in configurations:
```yaml
servers:
- name: "production_server"
command: "${SERVER_COMMAND}"
auth_token: "${AUTH_TOKEN}"
variables:
SERVER_COMMAND: "python -m prod_server"
AUTH_TOKEN: "bearer_token_here"
```
## 📊 Reporting & Output
### Console Output
Rich console output with real-time progress:
```bash
mcptesta test --server "my-server" --format console
```
### HTML Reports
Comprehensive HTML reports with interactive features:
```bash
mcptesta test --server "my-server" --format html --output ./reports
```
### Performance Profiling
Built-in memory and performance profiling:
```bash
mcptesta test --memory-profile --performance-profile --server "my-server"
```
### JUnit XML
Integration with CI/CD systems:
```bash
mcptesta test --format junit --output ./junit_results.xml --server "my-server"
```
## 🏗️ Architecture
MCPTesta is built with a modular, extensible architecture:
```
mcptesta/
├── core/ # Core client and session management
├── protocol/ # Advanced MCP protocol features
├── yaml_parser/ # YAML configuration parsing
├── runners/ # Parallel and sequential execution
├── reporters/ # Output formatting and reporting
└── utils/ # Utilities and helpers
```
### Key Components
- **MCPTestClient**: Advanced client with protocol feature detection
- **ParallelTestRunner**: Intelligent parallel execution with dependency resolution
- **ProtocolFeatures**: Comprehensive testing for advanced MCP features
- **YAMLTestParser**: Flexible configuration parsing with validation
## 🤝 Development
### Setup Development Environment
```bash
# Clone and setup
git clone <repo_url>
cd mcptesta
uv sync --dev
# Run tests
uv run pytest
# Format code
uv run black .
uv run ruff check .
# Type checking
uv run mypy src/
```
### Creating Custom Test Types
Extend MCPTesta with custom test types:
```python
from mcptesta.core.client import MCPTestClient, TestResult
class CustomTestRunner:
async def run_custom_test(self, client: MCPTestClient) -> TestResult:
# Implement custom testing logic
return TestResult(
test_name="custom_test",
success=True,
execution_time=1.0,
response_data={"custom": "result"}
)
```
## 📄 License
MIT License - see [LICENSE](LICENSE) for details.
## 🙏 Contributing
Contributions welcome! Please read our [Contributing Guide](CONTRIBUTING.md) for details.
## 🐛 Issues & Support
- **Bug Reports**: [Git Issues](https://git.supported.systems/mcp/mcptesta/issues)
- **Feature Requests**: [Git Discussions](https://git.supported.systems/mcp/mcptesta/discussions)
- **Documentation**: [MCPTesta Docs](https://mcptesta.l.supported.systems)
---
**MCPTesta** - Making FastMCP server testing comprehensive, reliable, and effortless. 🧪✨

123
SECURITY_AUDIT.md Normal file
View File

@ -0,0 +1,123 @@
# MCPTesta Security Audit - Ready for Public Repository
## 🔍 Pre-Publish Security Review
This document confirms MCPTesta has been thoroughly audited and is safe for public repository publication.
**Audit Date**: 2025-09-20
**Status**: ✅ CLEAN - Ready for public eyes
**Auditor**: Claude Code Assistant
## 🛡️ Security Checks Completed
### ✅ Sensitive Files & Credentials
- **No exposed credentials**: API keys, tokens, passwords not found in codebase
- **Environment files properly managed**: `.env` added to `.gitignore`, `.env.example` template provided
- **No private keys**: SSL certificates, SSH keys, signing keys not present
- **Virtual environment excluded**: `.venv/` properly ignored
### ✅ Configuration Security
- **Database connections**: No hardcoded database URLs or credentials
- **API endpoints**: No internal/private API endpoints exposed
- **Domain references**: Internal `.supported.systems` references updated to localhost for public use
- **Debug flags**: No debug tokens or development secrets
### ✅ Repository References
- **GitHub migration complete**: All references updated from GitHub to public Gitea instance
- **Support links updated**: Issues, discussions, documentation links point to public repositories
- **External dependencies**: Only references legitimate public repositories (FastMCP)
### ✅ Development Artifacts Cleaned
- **Temporary files removed**: Development-only files cleaned up
- **Logo assets organized**: Design specifications moved to proper asset structure
- **Documentation complete**: No internal-only documentation exposed
### ✅ Privacy & Personal Information
- **No personal data**: Email addresses, names, internal system details removed
- **Network references sanitized**: Internal network addresses replaced with localhost
- **Company specifics removed**: No internal company processes or private methodologies
## 📁 Files Safe for Public Consumption
### Core Project Files
- ✅ `README.md` - Clean, professional project description
- ✅ `pyproject.toml` - Standard Python packaging, no secrets
- ✅ `CLAUDE.md` - Comprehensive project context, no sensitive data
- ✅ `.gitignore` - Properly configured to exclude sensitive files
### Source Code
- ✅ `src/mcptesta/` - All Python source code clean
- ✅ `examples/` - Example configurations use placeholder values
- ✅ `tests/` - Test files contain no real credentials
- ✅ `scripts/` - Shell scripts use localhost references
### Documentation
- ✅ `docs/` - Complete Starlight documentation site
- ✅ All guides reference public resources only
- ✅ Installation instructions use public package managers
- ✅ API documentation shows public interfaces only
### Assets & Media
- ✅ `assets/logo/` - Complete logo package with proper licensing
- ✅ No proprietary design files or internal brand guidelines
- ✅ All images use community-appropriate content
## 🌐 Public Repository Readiness
### GitHub/Gitea Integration
- **Repository URLs**: All point to public Gitea instance at `git.supported.systems`
- **Issue tracking**: Public issue templates and contribution guidelines
- **CI/CD references**: Generic examples, no internal infrastructure details
- **Documentation links**: All point to publicly accessible resources
### Community-Focused Content
- **License**: MIT license allows public use and contribution
- **Contributing guidelines**: Welcome external contributors
- **Code of conduct**: Professional, inclusive community standards
- **Documentation**: Comprehensive, beginner-friendly guides
### Open Source Standards
- **Dependencies**: All dependencies are public, well-maintained packages
- **Build process**: Transparent, reproducible build system
- **Testing**: Public testing methodologies and examples
- **Packaging**: Standard Python packaging practices
## 🔐 Security Best Practices Implemented
### Access Control
- **Environment variables**: All secrets must be provided via environment
- **Configuration templates**: Examples use placeholder values
- **Authentication examples**: Show patterns, not real credentials
- **Network security**: No hardcoded internal network access
### Code Quality
- **Input validation**: Proper validation of user inputs
- **Error handling**: No sensitive information leaked in error messages
- **Logging**: Log statements don't expose sensitive data
- **Dependencies**: All dependencies from trusted public sources
## ✅ Final Clearance
**MCPTesta is ready for public repository publication** with confidence that:
1. **No sensitive information** will be exposed to public users
2. **No proprietary methods** or internal processes are revealed
3. **Community contributors** can safely engage with the project
4. **Enterprise users** can evaluate and deploy without security concerns
5. **Documentation** provides complete guidance without exposing internals
## 🚀 Recommended Next Steps
1. **Create public repository** on your chosen platform
2. **Push current state** - all files are clean and ready
3. **Set up issue templates** for community engagement
4. **Configure branch protection** for main/master branch
5. **Enable security scanning** (Dependabot, CodeQL)
---
**Security Clearance**: ✅ APPROVED
**Publication Status**: 🟢 READY
**Community Safety**: 🛡️ SECURED
*MCPTesta represents community-driven testing excellence while maintaining the highest standards of security and privacy.*

119
assets/logo/README.md Normal file
View File

@ -0,0 +1,119 @@
# MCPTesta Logo Assets
This directory contains the complete logo asset collection for the MCPTesta project, featuring the "Lab Experiment in Progress" design that represents community-driven testing excellence for the MCP ecosystem.
## 🧪 Design Concept
The MCPTesta logo depicts an active laboratory experiment with:
- **Erlenmeyer Flask**: Scientific beaker showing ongoing reactions
- **Bubbling Activity**: Dynamic bubbles representing test execution
- **Mini Test Tubes**: Parallel testing status indicators
- **Lab Apparatus**: Professional clamp and stand for authenticity
- **Community Colors**: Warm purple gradient with accessible cyan accents
## 📁 Directory Structure
```
logo/
├── source/ # Master files (SVG, design specs)
├── favicons/ # Web favicons and browser icons
├── app-icons/ # iOS and Android app icons
│ ├── ios/ # Apple App Store sizes
│ └── android/ # Google Play Store sizes
├── web/ # Web-optimized PNG/SVG files
├── social/ # Social media profile and card images
├── print/ # Print-ready files (CMYK, vector)
└── theme-variants/ # Dark, light, high-contrast versions
├── dark-theme/
├── light-theme/
└── high-contrast/
```
## 🎨 Usage Guidelines
### Primary Logo Usage
- Use the full-color version whenever possible
- Maintain minimum size of 32px for web contexts
- Preserve the lab experiment elements in all variations
- Use appropriate theme variants for dark/light contexts
### Color Specifications
- **Primary Background**: #6B46C1#8B5CF6 (purple gradient)
- **Active Liquid**: #0891B2#06B6D4 (teal to cyan)
- **Status Indicators**: Green (#10B981), Amber (#F59E0B), Red (#EF4444)
- **Glass/Metal**: Semi-transparent whites and grays
### Accessibility
- All versions meet WCAG AA contrast requirements
- High-contrast variants available for accessibility needs
- Colorblind-friendly status indicators provided
- Alternative text: "MCPTesta - Laboratory testing framework logo"
## 🚀 Quick Integration
### Documentation Site (Starlight)
```html
<!-- Site header -->
<img src="/assets/logo/web/mcptesta-horizontal-logo.svg"
alt="MCPTesta - Community-driven testing excellence" />
<!-- Favicon -->
<link rel="icon" type="image/svg+xml" href="/assets/logo/favicons/favicon.svg">
<link rel="icon" type="image/png" sizes="32x32" href="/assets/logo/favicons/favicon-32x32.png">
```
### README.md
```markdown
<div align="center">
<img src="assets/logo/web/mcptesta-logo-256px.png" alt="MCPTesta Logo" width="128" height="128">
<h1>MCPTesta</h1>
<p>Community-driven testing excellence for the MCP ecosystem</p>
</div>
```
### Social Media
- **Profile Pictures**: Use `social/profile-400x400.png`
- **Cover Images**: Use `social/header-1500x500.png`
- **Card Previews**: Use `social/card-1200x630.png`
## 📋 File Status
### ✅ Completed Specifications
- [x] Design specifications (`../logo-design-specs.md`)
- [x] Color variations (`../logo-color-variations.md`)
- [x] Contextual variants (`../logo-variations-guide.md`)
- [x] Export specifications (`../logo-export-specifications.md`)
- [x] Testing matrix (`../logo-testing-matrix.md`)
### 🔄 Pending Asset Creation
- [ ] Master SVG files
- [ ] Generated PNG exports
- [ ] Favicon package
- [ ] Social media assets
- [ ] Theme variants
## 🛠 Generation Scripts
Use the provided scripts to generate all logo assets:
```bash
# Generate all export formats
./scripts/generate-logo-exports.sh
# Run quality assurance checks
./scripts/qa-logo-check.sh
# Optimize for web delivery
./scripts/optimize-web-assets.sh
```
## 📞 Support
For logo usage questions or custom variations:
- Review the comprehensive specifications in parent directory
- Check the testing matrix for platform-specific guidance
- Ensure accessibility compliance for public-facing usage
---
*Created with scientific precision for the MCPTesta community* 🧪

View File

@ -0,0 +1,112 @@
# MCPTesta Social Media Assets
This directory contains social media preview assets for the MCPTesta project.
## 📱 Available Assets
### Profile Images
- **profile-400x400.svg** - Square profile image for social media platforms
- **profile-400x400.png** - PNG version for platforms requiring raster images
### Social Cards
- **card-1200x630.svg** - Twitter/X card template with logo and branding
- **card-1200x630.png** - PNG version for social media sharing
### Platform-Specific
- **github-social-1280x640.svg** - GitHub repository social preview
- **linkedin-cover-1584x396.svg** - LinkedIn company page cover
- **twitter-header-1500x500.svg** - Twitter/X profile header
## 🎨 Design Elements
All social media assets maintain the MCPTesta brand identity:
- **Lab Experiment Theme**: Active scientific testing with bubbling beakers
- **Community Colors**: Purple gradient (#6B46C1#8B5CF6) background
- **Testing Imagery**: Beakers, bubbles, and laboratory apparatus
- **Typography**: Clean, modern fonts with scientific credibility
## 📐 Specifications
### Profile Images (400x400px)
```
Format: SVG + PNG fallback
Aspect Ratio: 1:1 (square)
Safe Area: 360x360px (10% margin)
Background: Full MCPTesta logo with community purple gradient
```
### Social Cards (1200x630px)
```
Format: SVG + PNG fallback
Aspect Ratio: 1.91:1 (Twitter/Facebook standard)
Logo Position: Left third (300px width)
Text Area: Right two-thirds with tagline and description
Background: Community gradient with subtle lab equipment patterns
```
### GitHub Social Preview (1280x640px)
```
Format: SVG + PNG fallback
Aspect Ratio: 2:1 (GitHub standard)
Background: Dark theme (#0D1117) to match GitHub
Logo: Adapted for dark background with enhanced glow
Text: Repository description and key features
```
## 🔄 Generation Process
These assets can be generated using the export scripts:
```bash
# Generate all social media assets
./scripts/generate-logo-exports.sh
# Create platform-specific variations
./scripts/generate-social-assets.sh
```
## 🎯 Usage Guidelines
### Twitter/X
- **Profile**: Use profile-400x400.png
- **Header**: Use twitter-header-1500x500.png
- **Cards**: Automatic generation from card-1200x630.png
### GitHub/Git
- **Repository Social**: Use github-social-1280x640.png
- **README**: Use assets/logo/web/mcptesta-logo.svg
### LinkedIn
- **Company Profile**: Use profile-400x400.png
- **Cover Image**: Use linkedin-cover-1584x396.png
- **Post Images**: Use card-1200x630.png
### Facebook/Meta
- **Profile**: Use profile-400x400.png
- **Cover**: Use card-1200x630.png (may need cropping)
- **Shared Links**: Automatic preview from card-1200x630.png
## 📝 Asset Status
### ✅ Created
- [ ] profile-400x400.svg
- [ ] profile-400x400.png
- [ ] card-1200x630.svg
- [ ] card-1200x630.png
### 🔄 Pending
- [ ] github-social-1280x640.svg
- [ ] linkedin-cover-1584x396.svg
- [ ] twitter-header-1500x500.svg
- [ ] PNG exports for all SVG files
### 🎯 Future Enhancements
- [ ] Animated GIF versions for supported platforms
- [ ] Platform-specific color adaptations
- [ ] Localized versions for international markets
- [ ] A/B testing variations for conversion optimization
---
*These assets represent the community-driven spirit and scientific excellence of the MCPTesta project* 🧪

View File

@ -0,0 +1,124 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg viewBox="0 0 400 400" xmlns="http://www.w3.org/2000/svg">
<defs>
<!-- Background Gradient -->
<linearGradient id="bgGradient" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#6B46C1"/>
<stop offset="100%" stop-color="#8B5CF6"/>
</linearGradient>
<!-- Liquid Gradient -->
<linearGradient id="liquidGradient" x1="0%" y1="100%" x2="0%" y2="0%">
<stop offset="0%" stop-color="#0891B2"/>
<stop offset="60%" stop-color="#06B6D4"/>
<stop offset="100%" stop-color="#22D3EE"/>
</linearGradient>
<!-- Glass Material -->
<linearGradient id="glassGradient" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="rgba(255,255,255,0.4)"/>
<stop offset="50%" stop-color="rgba(255,255,255,0.1)"/>
<stop offset="100%" stop-color="rgba(255,255,255,0.2)"/>
</linearGradient>
<!-- Metal Apparatus -->
<linearGradient id="metalGradient" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#94A3B8"/>
<stop offset="100%" stop-color="#64748B"/>
</linearGradient>
<!-- Glow Effects -->
<filter id="liquidGlow">
<feGaussianBlur stdDeviation="8" result="coloredBlur"/>
<feMerge>
<feMergeNode in="coloredBlur"/>
<feMergeNode in="SourceGraphic"/>
</feMerge>
</filter>
<filter id="bubbleGlow">
<feGaussianBlur stdDeviation="4" result="coloredBlur"/>
<feMerge>
<feMergeNode in="coloredBlur"/>
<feMergeNode in="SourceGraphic"/>
</feMerge>
</filter>
</defs>
<!-- Background with rounded corners -->
<rect width="400" height="400" rx="80" ry="80" fill="url(#bgGradient)"/>
<!-- Scale factor: 4x from original 100x100 design -->
<g transform="translate(50, 50) scale(3)">
<!-- Lab Stand/Apparatus -->
<g id="labStand">
<!-- Horizontal support bar -->
<rect x="20" y="35" width="60" height="2" fill="url(#metalGradient)" rx="1"/>
<!-- Vertical support -->
<rect x="75" y="25" width="2" height="15" fill="url(#metalGradient)" rx="1"/>
<!-- Clamp mechanism -->
<rect x="73" y="33" width="6" height="4" fill="url(#metalGradient)" rx="1"/>
</g>
<!-- Main Beaker (Erlenmeyer Flask) -->
<g id="mainBeaker">
<!-- Flask body -->
<path d="M35 75 L35 45 L40 35 L60 35 L65 45 L65 75 Z"
fill="url(#glassGradient)" stroke="rgba(255,255,255,0.3)" stroke-width="0.5"/>
<!-- Liquid inside -->
<path d="M37 72 L37 50 L41 40 L59 40 L63 50 L63 72 Z"
fill="url(#liquidGradient)" filter="url(#liquidGlow)"/>
<!-- Pour spout -->
<path d="M60 35 Q65 32 63 30"
fill="none" stroke="rgba(255,255,255,0.3)" stroke-width="1"/>
<!-- Graduation marks -->
<line x1="67" y1="45" x2="69" y2="45" stroke="rgba(255,255,255,0.3)" stroke-width="0.5"/>
<line x1="67" y1="55" x2="69" y2="55" stroke="rgba(255,255,255,0.3)" stroke-width="0.5"/>
<line x1="67" y1="65" x2="69" y2="65" stroke="rgba(255,255,255,0.3)" stroke-width="0.5"/>
</g>
<!-- Bubbles (Active Reaction) -->
<g id="bubbles">
<!-- Large bubbles in liquid -->
<circle cx="45" cy="55" r="3" fill="rgba(255,255,255,0.6)" filter="url(#bubbleGlow)"/>
<circle cx="55" cy="50" r="2" fill="rgba(255,255,255,0.4)" filter="url(#bubbleGlow)"/>
<circle cx="42" cy="65" r="2.5" fill="rgba(255,255,255,0.5)" filter="url(#bubbleGlow)"/>
<!-- Rising bubbles -->
<circle cx="48" cy="42" r="1.5" fill="rgba(255,255,255,0.7)"/>
<circle cx="52" cy="38" r="1" fill="rgba(255,255,255,0.8)"/>
<!-- Escaping bubbles -->
<circle cx="46" cy="30" r="0.8" fill="rgba(255,255,255,0.6)"/>
<circle cx="54" cy="28" r="0.5" fill="rgba(255,255,255,0.5)"/>
</g>
<!-- Mini Test Tubes (Status Indicators) -->
<g id="statusTubes">
<!-- Success tube (green) -->
<rect x="15" y="70" width="4" height="12" fill="rgba(255,255,255,0.2)" rx="2"/>
<rect x="16" y="75" width="2" height="6" fill="#10B981" rx="1"/>
<!-- Warning tube (amber) -->
<rect x="22" y="72" width="4" height="10" fill="rgba(255,255,255,0.2)" rx="2"/>
<rect x="23" y="76" width="2" height="5" fill="#F59E0B" rx="1"/>
<!-- Error tube (red) -->
<rect x="81" y="71" width="4" height="11" fill="rgba(255,255,255,0.2)" rx="2"/>
<rect x="82" y="76" width="2" height="5" fill="#EF4444" rx="1"/>
</g>
<!-- Surface Reflections -->
<g id="reflections" opacity="0.3">
<!-- Main beaker highlight -->
<path d="M37 35 Q42 32 47 35 Q52 38 47 40 Q42 37 37 35"
fill="rgba(255,255,255,0.4)"/>
<!-- Liquid surface reflection -->
<ellipse cx="50" cy="42" rx="8" ry="1" fill="rgba(255,255,255,0.2)"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.8 KiB

View File

@ -0,0 +1,120 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<defs>
<!-- Background Gradient -->
<linearGradient id="bgGradient" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#6B46C1"/>
<stop offset="100%" stop-color="#8B5CF6"/>
</linearGradient>
<!-- Liquid Gradient -->
<linearGradient id="liquidGradient" x1="0%" y1="100%" x2="0%" y2="0%">
<stop offset="0%" stop-color="#0891B2"/>
<stop offset="60%" stop-color="#06B6D4"/>
<stop offset="100%" stop-color="#22D3EE"/>
</linearGradient>
<!-- Glass Material -->
<linearGradient id="glassGradient" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="rgba(255,255,255,0.4)"/>
<stop offset="50%" stop-color="rgba(255,255,255,0.1)"/>
<stop offset="100%" stop-color="rgba(255,255,255,0.2)"/>
</linearGradient>
<!-- Metal Apparatus -->
<linearGradient id="metalGradient" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#94A3B8"/>
<stop offset="100%" stop-color="#64748B"/>
</linearGradient>
<!-- Glow Effects -->
<filter id="liquidGlow">
<feGaussianBlur stdDeviation="2" result="coloredBlur"/>
<feMerge>
<feMergeNode in="coloredBlur"/>
<feMergeNode in="SourceGraphic"/>
</feMerge>
</filter>
<filter id="bubbleGlow">
<feGaussianBlur stdDeviation="1" result="coloredBlur"/>
<feMerge>
<feMergeNode in="coloredBlur"/>
<feMergeNode in="SourceGraphic"/>
</feMerge>
</filter>
</defs>
<!-- Background with rounded corners -->
<rect width="100" height="100" rx="20" ry="20" fill="url(#bgGradient)"/>
<!-- Lab Stand/Apparatus -->
<g id="labStand">
<!-- Horizontal support bar -->
<rect x="20" y="35" width="60" height="2" fill="url(#metalGradient)" rx="1"/>
<!-- Vertical support -->
<rect x="75" y="25" width="2" height="15" fill="url(#metalGradient)" rx="1"/>
<!-- Clamp mechanism -->
<rect x="73" y="33" width="6" height="4" fill="url(#metalGradient)" rx="1"/>
</g>
<!-- Main Beaker (Erlenmeyer Flask) -->
<g id="mainBeaker">
<!-- Flask body -->
<path d="M35 75 L35 45 L40 35 L60 35 L65 45 L65 75 Z"
fill="url(#glassGradient)" stroke="rgba(255,255,255,0.3)" stroke-width="0.5"/>
<!-- Liquid inside -->
<path d="M37 72 L37 50 L41 40 L59 40 L63 50 L63 72 Z"
fill="url(#liquidGradient)" filter="url(#liquidGlow)"/>
<!-- Pour spout -->
<path d="M60 35 Q65 32 63 30"
fill="none" stroke="rgba(255,255,255,0.3)" stroke-width="1"/>
<!-- Graduation marks -->
<line x1="67" y1="45" x2="69" y2="45" stroke="rgba(255,255,255,0.3)" stroke-width="0.5"/>
<line x1="67" y1="55" x2="69" y2="55" stroke="rgba(255,255,255,0.3)" stroke-width="0.5"/>
<line x1="67" y1="65" x2="69" y2="65" stroke="rgba(255,255,255,0.3)" stroke-width="0.5"/>
</g>
<!-- Bubbles (Active Reaction) -->
<g id="bubbles">
<!-- Large bubbles in liquid -->
<circle cx="45" cy="55" r="3" fill="rgba(255,255,255,0.6)" filter="url(#bubbleGlow)"/>
<circle cx="55" cy="50" r="2" fill="rgba(255,255,255,0.4)" filter="url(#bubbleGlow)"/>
<circle cx="42" cy="65" r="2.5" fill="rgba(255,255,255,0.5)" filter="url(#bubbleGlow)"/>
<!-- Rising bubbles -->
<circle cx="48" cy="42" r="1.5" fill="rgba(255,255,255,0.7)"/>
<circle cx="52" cy="38" r="1" fill="rgba(255,255,255,0.8)"/>
<!-- Escaping bubbles -->
<circle cx="46" cy="30" r="0.8" fill="rgba(255,255,255,0.6)"/>
<circle cx="54" cy="28" r="0.5" fill="rgba(255,255,255,0.5)"/>
</g>
<!-- Mini Test Tubes (Status Indicators) -->
<g id="statusTubes">
<!-- Success tube (green) -->
<rect x="15" y="70" width="4" height="12" fill="rgba(255,255,255,0.2)" rx="2"/>
<rect x="16" y="75" width="2" height="6" fill="#10B981" rx="1"/>
<!-- Warning tube (amber) -->
<rect x="22" y="72" width="4" height="10" fill="rgba(255,255,255,0.2)" rx="2"/>
<rect x="23" y="76" width="2" height="5" fill="#F59E0B" rx="1"/>
<!-- Error tube (red) -->
<rect x="81" y="71" width="4" height="11" fill="rgba(255,255,255,0.2)" rx="2"/>
<rect x="82" y="76" width="2" height="5" fill="#EF4444" rx="1"/>
</g>
<!-- Surface Reflections -->
<g id="reflections" opacity="0.3">
<!-- Main beaker highlight -->
<path d="M37 35 Q42 32 47 35 Q52 38 47 40 Q42 37 37 35"
fill="rgba(255,255,255,0.4)"/>
<!-- Liquid surface reflection -->
<ellipse cx="50" cy="42" rx="8" ry="1" fill="rgba(255,255,255,0.2)"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.6 KiB

43
config/fluent-bit.conf Normal file
View File

@ -0,0 +1,43 @@
# Fluent Bit configuration for production logging
[SERVICE]
Flush 1
Log_Level info
Daemon off
Parsers_File parsers.conf
[INPUT]
Name tail
Path /var/lib/docker/containers/*/*.log
Parser docker
Tag docker.*
Refresh_Interval 5
Mem_Buf_Limit 50MB
Skip_Long_Lines On
[FILTER]
Name modify
Match docker.*
Add service mcptesta-docs
Add environment ${NODE_ENV}
[FILTER]
Name grep
Match docker.*
Regex log level=(error|warn|info)
[OUTPUT]
Name stdout
Match *
Format json_lines
# Optional: Send to external logging service
# [OUTPUT]
# Name http
# Match *
# Host your-logging-service.com
# Port 443
# URI /api/v1/logs
# Header Authorization Bearer YOUR_TOKEN
# Format json
# tls on
# tls.verify on

34
config/nginx-dev.conf Normal file
View File

@ -0,0 +1,34 @@
# Development Nginx configuration for LiveReload
events {
worker_connections 1024;
}
http {
upstream docs {
server docs:4321;
}
server {
listen 35729;
server_name localhost;
# LiveReload WebSocket proxy
location /livereload {
proxy_pass http://docs;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# Health check
location /health {
access_log off;
return 200 "livereload healthy\n";
add_header Content-Type text/plain;
}
}
}

132
docker-compose.dev.yml Normal file
View File

@ -0,0 +1,132 @@
# Development override for MCPTesta Docker Compose
# Use with: docker compose -f docker-compose.yml -f docker-compose.dev.yml up
services:
docs:
build:
target: development
args:
NODE_ENV: development
environment:
NODE_ENV: development
# Enable Astro development features
ASTRO_TELEMETRY_DISABLED: 1
# Development debugging
DEBUG: "astro:*"
# Development volume mounts for hot reloading
volumes:
- ./docs:/app
- /app/node_modules # Prevent host node_modules from overriding container
- docs_dev_cache:/app/.astro # Cache Astro build artifacts
# Development ports (exposed for debugging)
ports:
- "4321:4321" # Astro dev server
- "9229:9229" # Node.js debugging port
# Development command with debugging
command: ["npm", "run", "dev", "--", "--host", "0.0.0.0", "--port", "4321", "--verbose"]
# Relaxed security for development
security_opt: []
read_only: false
user: "1000:1000"
# Development labels (less caching, more verbose)
labels:
caddy: ${DOCS_DOMAIN:-mcptesta.l.supported.systems}
caddy.reverse_proxy: "{{upstreams 4321}}"
caddy.encode: gzip
caddy.header.Cache-Control: "no-cache, no-store, must-revalidate"
caddy.header.X-Dev-Mode: "true"
# Development resource limits (more generous)
deploy:
resources:
limits:
cpus: '2.0'
memory: 1g
reservations:
memory: 512m
# Development file watcher (optional)
docs-watcher:
image: node:20-alpine
working_dir: /app
volumes:
- ./docs:/app
command: >
sh -c "
apk add --no-cache inotify-tools &&
while true; do
inotifywait -r -e modify,create,delete ./src &&
echo '🔄 Files changed, Astro will auto-reload...'
done
"
profiles:
- watcher
networks:
- monitoring
# Development database for testing (SQLite in volume)
dev-db:
image: alpine:latest
volumes:
- dev_data:/data
command: >
sh -c "
mkdir -p /data &&
touch /data/mcptesta-dev.db &&
echo 'Development database ready at /data/mcptesta-dev.db' &&
tail -f /dev/null
"
profiles:
- database
networks:
- internal
# Development Redis for caching tests
dev-redis:
image: redis:7-alpine
ports:
- "6379:6379"
volumes:
- dev_redis:/data
command: redis-server --appendonly yes --maxmemory 128mb --maxmemory-policy allkeys-lru
profiles:
- cache
networks:
- internal
deploy:
resources:
limits:
memory: 128m
# Live reload proxy for enhanced development
livereload:
image: nginx:alpine
volumes:
- ./config/nginx-dev.conf:/etc/nginx/nginx.conf:ro
ports:
- "35729:35729" # LiveReload port
profiles:
- livereload
networks:
- caddy
depends_on:
- docs
volumes:
# Development-specific volumes
docs_dev_cache:
driver: local
name: ${COMPOSE_PROJECT}_docs_dev_cache
dev_data:
driver: local
name: ${COMPOSE_PROJECT}_dev_data
dev_redis:
driver: local
name: ${COMPOSE_PROJECT}_dev_redis

96
docker-compose.prod.yml Normal file
View File

@ -0,0 +1,96 @@
# Production override for MCPTesta Docker Compose
# Use with: docker compose -f docker-compose.yml -f docker-compose.prod.yml up
services:
docs:
build:
target: production
args:
NODE_ENV: production
environment:
NODE_ENV: production
# Remove development volume mounts
volumes: []
# Production resource limits
deploy:
replicas: 2
update_config:
parallelism: 1
failure_action: rollback
delay: 10s
order: start-first
restart_policy:
condition: on-failure
delay: 5s
max_attempts: 3
resources:
limits:
cpus: '1.0'
memory: 256m
reservations:
cpus: '0.25'
memory: 128m
# Enhanced security for production
security_opt:
- no-new-privileges:true
- apparmor:docker-default
# Read-only filesystem for production
read_only: true
tmpfs:
- /tmp:noexec,nosuid,size=50m
- /var/cache/nginx:noexec,nosuid,size=10m
- /var/log/nginx:noexec,nosuid,size=10m
# Production labels
labels:
caddy: ${DOCS_DOMAIN:-mcptesta.l.supported.systems}
caddy.reverse_proxy: "{{upstreams 4321}}"
caddy.encode: "gzip zstd"
caddy.header.Cache-Control: "public, max-age=31536000, immutable"
caddy.header.Strict-Transport-Security: "max-age=31536000; includeSubDomains; preload"
caddy.header.X-Frame-Options: "SAMEORIGIN"
caddy.header.X-Content-Type-Options: "nosniff"
caddy.header.X-XSS-Protection: "1; mode=block"
caddy.header.Referrer-Policy: "strict-origin-when-cross-origin"
# Rate limiting for production
caddy.rate_limit: "zone docs_zone key {remote_host} events 1000 window 1h"
# Production monitoring service
docs-monitor:
image: prom/node-exporter:latest
command:
- '--path.rootfs=/host'
- '--collector.filesystem.mount-points-exclude=^/(sys|proc|dev|host|etc)($$|/)'
volumes:
- '/:/host:ro,rslave'
networks:
- monitoring
restart: unless-stopped
deploy:
resources:
limits:
cpus: '0.1'
memory: 64m
security_opt:
- no-new-privileges:true
read_only: true
# Log aggregation for production
docs-logs:
image: fluent/fluent-bit:latest
volumes:
- /var/lib/docker/containers:/var/lib/docker/containers:ro
- ./config/fluent-bit.conf:/fluent-bit/etc/fluent-bit.conf:ro
networks:
- monitoring
restart: unless-stopped
profiles:
- logging
deploy:
resources:
limits:
cpus: '0.1'
memory: 128m

104
docker-compose.yml Normal file
View File

@ -0,0 +1,104 @@
# MCPTesta Docker Compose Configuration
# Modern Docker Compose without version attribute
x-logging: &default-logging
driver: json-file
options:
max-size: "${LOG_MAX_SIZE:-10m}"
max-file: "${LOG_MAX_FILES:-3}"
x-healthcheck: &default-healthcheck
interval: ${HEALTH_CHECK_INTERVAL:-30s}
timeout: ${HEALTH_CHECK_TIMEOUT:-10s}
retries: ${HEALTH_CHECK_RETRIES:-3}
start_period: ${HEALTH_CHECK_START_PERIOD:-40s}
services:
# Documentation Site
docs:
build:
context: ./docs
dockerfile: Dockerfile
target: ${NODE_ENV:-development}
args:
NODE_ENV: ${NODE_ENV:-development}
environment:
NODE_ENV: ${NODE_ENV:-development}
HOST: ${DOCS_HOST:-0.0.0.0}
PORT: ${DOCS_PORT:-4321}
DOMAIN: ${DOMAIN:-mcptesta.l.supported.systems}
labels:
# Caddy Docker Proxy configuration
caddy: ${DOCS_DOMAIN:-mcptesta.l.supported.systems}
caddy.reverse_proxy: "{{upstreams 4321}}"
caddy.encode: gzip
caddy.header.Cache-Control: "public, max-age=31536000"
caddy.header.X-Frame-Options: "SAMEORIGIN"
caddy.header.X-Content-Type-Options: "nosniff"
volumes:
# Development: Mount source for hot reloading
- ./docs:/app:${DEV_WATCH_ENABLED:-true}
# Exclude node_modules from host mount
- /app/node_modules
healthcheck:
<<: *default-healthcheck
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:4321/"]
logging: *default-logging
networks:
- caddy
- monitoring
restart: unless-stopped
deploy:
resources:
limits:
cpus: ${DOCS_CPU_LIMIT:-0.5}
memory: ${DOCS_MEMORY_LIMIT:-512m}
reservations:
memory: 256m
# Security settings
security_opt:
- no-new-privileges:true
read_only: false # Astro needs write access for builds
tmpfs:
- /tmp:noexec,nosuid,size=100m
user: "1000:1000"
# Optional: Documentation builder for production builds
docs-builder:
build:
context: ./docs
dockerfile: Dockerfile
target: builder
environment:
NODE_ENV: production
volumes:
- ./docs:/app
- docs_build:/app/dist
profiles:
- build
command: npm run build
networks:
- internal
networks:
# External Caddy network for reverse proxy
caddy:
external: true
name: caddy
# Monitoring network
monitoring:
driver: bridge
name: ${COMPOSE_PROJECT}_monitoring
# Internal network for build processes
internal:
driver: bridge
internal: true
name: ${COMPOSE_PROJECT}_internal
volumes:
# Production build artifacts
docs_build:
driver: local
name: ${COMPOSE_PROJECT}_docs_build

70
docs/.dockerignore Normal file
View File

@ -0,0 +1,70 @@
# MCPTesta Docs Docker Ignore
# Optimize Docker builds by excluding unnecessary files
# Node.js
node_modules
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.npm
# Build outputs
dist/
build/
.astro/
# Development files
.env
.env.local
.env.development.local
.env.test.local
.env.production.local
# IDE and editor files
.vscode/
.idea/
*.swp
*.swo
*~
# OS generated files
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
ehthumbs.db
Thumbs.db
# Git
.git/
.gitignore
# Documentation files (not needed in container)
README.md
STRUCTURE.md
*.md
# Test files
test/
tests/
**/*.test.js
**/*.spec.js
# Coverage reports
coverage/
.nyc_output/
# Logs
logs/
*.log
# Runtime data
pids/
*.pid
*.seed
*.pid.lock
# Temporary files
tmp/
temp/

129
docs/Dockerfile Normal file
View File

@ -0,0 +1,129 @@
# Multi-stage Dockerfile for MCPTesta Documentation
# Supports both development and production modes
# Base stage with Node.js
FROM node:20-alpine AS base
WORKDIR /app
# Install system dependencies
RUN apk add --no-cache \
dumb-init \
curl \
wget \
&& rm -rf /var/cache/apk/*
# Create non-root user
RUN addgroup -g 1001 -S nodejs && \
adduser -S astro -u 1001 -G nodejs
# Copy package files
COPY package*.json ./
# Dependencies stage
FROM base AS deps
RUN npm ci --only=production && npm cache clean --force
# Development dependencies stage
FROM base AS dev-deps
RUN npm ci && npm cache clean --force
# Builder stage for production builds
FROM dev-deps AS builder
COPY . .
RUN npm run build
# Development stage with hot reloading
FROM dev-deps AS development
COPY . .
# Change ownership to non-root user
RUN chown -R astro:nodejs /app
USER astro
EXPOSE 4321
# Health check
HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \
CMD wget --no-verbose --tries=1 --spider http://localhost:4321/ || exit 1
# Use dumb-init for proper signal handling
ENTRYPOINT ["dumb-init", "--"]
CMD ["npm", "run", "dev", "--", "--host", "0.0.0.0", "--port", "4321"]
# Production stage with static files
FROM nginx:alpine AS production
# Install security updates
RUN apk update && apk upgrade && \
apk add --no-cache dumb-init && \
rm -rf /var/cache/apk/*
# Copy built site from builder stage
COPY --from=builder /app/dist /usr/share/nginx/html
# Custom nginx configuration for Astro
COPY <<EOF /etc/nginx/conf.d/default.conf
server {
listen 4321;
server_name localhost;
root /usr/share/nginx/html;
index index.html;
# Security headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
# Gzip compression
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_types
text/plain
text/css
text/xml
text/javascript
application/javascript
application/xml+rss
application/json;
# Cache static assets
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
# Handle client-side routing
location / {
try_files \$uri \$uri/ /index.html;
}
# Health check endpoint
location /health {
access_log off;
return 200 "healthy\n";
add_header Content-Type text/plain;
}
}
EOF
# Create non-root user for nginx
RUN addgroup -g 1001 -S nginx_user && \
adduser -S nginx_user -u 1001 -G nginx_user
# Fix permissions
RUN chown -R nginx_user:nginx_user /usr/share/nginx/html && \
chown -R nginx_user:nginx_user /var/cache/nginx && \
chown -R nginx_user:nginx_user /var/log/nginx && \
chown -R nginx_user:nginx_user /etc/nginx/conf.d
USER nginx_user
EXPOSE 4321
# Health check
HEALTHCHECK --interval=30s --timeout=10s --start-period=30s --retries=3 \
CMD wget --no-verbose --tries=1 --spider http://localhost:4321/health || exit 1
ENTRYPOINT ["dumb-init", "--"]
CMD ["nginx", "-g", "daemon off;"]

233
docs/README.md Normal file
View File

@ -0,0 +1,233 @@
# MCPTesta Documentation
This directory contains the comprehensive documentation for MCPTesta, built with [Astro](https://astro.build/) and [Starlight](https://starlight.astro.build/) following the [Diátaxis](https://diataxis.fr/) documentation framework.
## Documentation Structure
The documentation is organized according to Diátaxis principles, with four distinct types of content serving different user needs:
### 📚 Diátaxis Organization
#### 🎓 Tutorials (Learning-oriented)
*"Can you teach me to...?"*
Step-by-step lessons that take the reader by the hand through a series of steps to complete a project:
- **[Your First Test](/tutorials/first-test/)** - Write and run your first MCPTesta test
- **[Testing Walkthrough](/tutorials/testing-walkthrough/)** - Explore MCPTesta's testing capabilities
- **[YAML Configuration](/tutorials/yaml-configuration/)** - Master advanced configurations
- **[Parallel Testing](/tutorials/parallel-testing/)** - Optimize test execution performance
#### 🔧 How-to Guides (Problem-oriented)
*"How do I...?"*
Practical guides that show how to solve specific problems:
- **[Test Production Servers](/how-to/test-production-servers/)** - Safe production testing strategies
- **[CI/CD Integration](/how-to/ci-cd-integration/)** - Automate testing in pipelines
- **[Troubleshooting](/how-to/troubleshooting/)** - Diagnose and resolve issues
#### 📖 Reference (Information-oriented)
*"What are the options for...?"*
Complete and accurate descriptions of the machinery:
- **[CLI Reference](/reference/cli/)** - Complete command-line interface documentation
- **[YAML Reference](/reference/yaml/)** - Comprehensive configuration format specification
- **[API Reference](/reference/api/)** - Full Python API documentation
#### 💡 Explanation (Understanding-oriented)
*"Why does this work this way?"*
Discussions that clarify and illuminate how and why:
- **[MCP Protocol Testing](/explanation/mcp-protocol/)** - Understanding MCP protocol testing concepts
- **[Architecture](/explanation/architecture/)** - MCPTesta's design and architectural decisions
- **[Testing Strategies](/explanation/testing-strategies/)** - Methodologies and approaches
## Development
### Prerequisites
- Node.js 18+
- npm or yarn
### Local Development
```bash
# Install dependencies
npm install
# Start development server
npm run dev
# Build for production
npm run build
# Preview production build
npm run preview
```
### Content Structure
```
docs/
├── src/
│ ├── content/
│ │ └── docs/
│ │ ├── introduction.md
│ │ ├── installation.md
│ │ ├── tutorials/
│ │ ├── how-to/
│ │ ├── reference/
│ │ ├── explanation/
│ │ └── community/
│ ├── styles/
│ │ └── custom.css
│ └── assets/
├── astro.config.mjs
├── package.json
└── README.md
```
## Writing Guidelines
### Diátaxis Principles
When contributing to the documentation, ensure content follows Diátaxis principles:
**Tutorials:**
- Use "we" language and hands-on approach
- Provide reliable, tested step-by-step instructions
- Focus on learning, not teaching
- Build confidence through visible results
- Minimize explanation
**How-to Guides:**
- Start with the user's problem
- Provide a series of steps to solve it
- Focus on practical usability over completeness
- Handle real-world complexity
- Write from the user's perspective
**Reference:**
- Be neutral and authoritative
- Mirror the product's structure
- Avoid instruction or explanation
- Provide complete, accurate information
- Optimize for consultation
**Explanation:**
- Make connections between concepts
- Provide higher perspective
- Be readable "in the bath"
- Answer "Can you tell me about...?" questions
- Offer informed opinions and context
### Content Guidelines
**Clarity**: Write for developers of all skill levels
**Completeness**: Cover the full scope of each topic
**Currency**: Keep examples and information up to date
**Consistency**: Use consistent terminology and formatting
**Accessibility**: Ensure content is accessible to all users
### Code Examples
**Working Examples**: All code examples should be tested and functional
**Context**: Provide sufficient context for examples to be understood
**Comments**: Include helpful comments in complex examples
**Error Handling**: Show both success and error scenarios
### File Naming
Use kebab-case for all files:
- `testing-walkthrough.md`
- `ci-cd-integration.md`
- `mcp-protocol.md`
## Contributing
### Content Contributions
1. **Identify the content type** using Diátaxis principles
2. **Check existing content** to avoid duplication
3. **Follow the appropriate style** for the content type
4. **Include working examples** and test them
5. **Submit a pull request** with clear description
### Documentation Issues
If you find issues with the documentation:
1. **Check existing issues** to avoid duplicates
2. **Provide specific details** about the problem
3. **Suggest improvements** when possible
4. **Include context** about your use case
### Style and Formatting
**Markdown**: Use standard Markdown syntax
**Headers**: Use sentence case for headers
**Links**: Use descriptive link text
**Images**: Include alt text for accessibility
**Code**: Use syntax highlighting with language specification
## Deployment
The documentation is automatically built and deployed on:
- **Main branch**: Production deployment
- **Pull requests**: Preview deployments for review
### Manual Deployment
```bash
# Build the site
npm run build
# Deploy to hosting platform
# (specific commands depend on your hosting choice)
```
## Technology Stack
**[Astro](https://astro.build/)**: Static site generator with excellent performance
**[Starlight](https://starlight.astro.build/)**: Documentation theme optimized for technical content
**[Diátaxis](https://diataxis.fr/)**: Documentation methodology for user-centered organization
### Why These Choices?
**Astro**: Fast, modern static site generation with excellent SEO and performance
**Starlight**: Purpose-built for technical documentation with excellent navigation and search
**Diátaxis**: Proven methodology that serves different user mental states effectively
## Maintenance
### Regular Updates
- **Content Review**: Quarterly review of all content for accuracy
- **Link Checking**: Automated checking for broken links
- **Example Testing**: Regular validation of code examples
- **User Feedback**: Incorporate feedback from the community
### Analytics and Monitoring
- **Usage Tracking**: Monitor which content is most valuable
- **Search Analytics**: Understand what users are looking for
- **Performance Monitoring**: Ensure fast loading times
- **User Feedback**: Collect and respond to user input
## Support
For documentation-specific questions:
- **GitHub Issues**: Report bugs or request new content
- **GitHub Discussions**: Ask questions about the documentation
- **Pull Requests**: Contribute improvements directly
For MCPTesta usage questions, see the main project documentation and support channels.
---
*This documentation is built with ❤️ by the MCPTesta community following Diátaxis principles to serve all FastMCP developers effectively.*

237
docs/STRUCTURE.md Normal file
View File

@ -0,0 +1,237 @@
# MCPTesta Documentation Structure
This document provides a complete overview of the Diátaxis-compliant documentation structure created for MCPTesta.
## 📋 Structure Overview
The documentation follows the [Diátaxis framework](https://diataxis.fr/) to serve four distinct user mental states:
```
docs/
├── 🏗️ Technical Setup
│ ├── astro.config.mjs # Astro/Starlight configuration
│ ├── package.json # Node.js dependencies
│ └── src/
│ ├── styles/
│ │ └── custom.css # Diátaxis-aware styling
│ └── content/docs/
├── 🏠 Getting Started
│ ├── introduction.md # Main landing page
│ └── installation.md # Installation guide
├── 🎓 Tutorials (Learning-oriented)
│ ├── first-test.md # Your first MCPTesta test
│ ├── testing-walkthrough.md # Explore all capabilities
│ ├── yaml-configuration.md # Master advanced configs
│ └── parallel-testing.md # Optimize performance
├── 🔧 How-to Guides (Problem-oriented)
│ ├── test-production-servers.md # Safe production testing
│ ├── ci-cd-integration.md # Automate testing
│ └── troubleshooting.md # Diagnose and resolve issues
├── 📖 Reference (Information-oriented)
│ ├── cli.md # Complete CLI documentation
│ ├── yaml.md # Full YAML reference
│ └── api.md # Python API reference
├── 💡 Explanation (Understanding-oriented)
│ ├── mcp-protocol.md # Protocol testing concepts
│ ├── architecture.md # Design and decisions
│ └── testing-strategies.md # Methodologies and approaches
└── 🤝 Community
├── contributing.md # Contribution guidelines
└── changelog.md # Version history
```
## 🎯 Diátaxis Compliance Analysis
### ✅ Tutorials (Learning-oriented)
**Purpose**: Take newcomers by the hand through their first experiences
**Key Characteristics**:
- **Hands-on approach**: All tutorials include actual code and commands
- **"We" language**: Used throughout to create collaborative feeling
- **Visible results**: Each step shows immediate feedback
- **Reliable progression**: Tested step-by-step instructions
- **Minimal explanation**: Focus on doing, not understanding
**Content Created**:
1. **Your First Test** - Complete beginner experience with working echo server
2. **Testing Walkthrough** - Comprehensive exploration of all MCPTesta capabilities
3. **YAML Configuration** - Progressive complexity from basic to advanced configs
4. **Parallel Testing** - Hands-on performance optimization
### ✅ How-to Guides (Problem-oriented)
**Purpose**: Solve specific real-world problems users encounter
**Key Characteristics**:
- **Problem-focused**: Each guide starts with a specific scenario
- **Practical solutions**: Working code and configurations
- **Real-world complexity**: Handles authentication, security, edge cases
- **User perspective**: Written from the practitioner's viewpoint
- **Goal-oriented**: Clear outcomes and success criteria
**Content Created**:
1. **Test Production Servers** - Safe strategies for live system validation
2. **CI/CD Integration** - Complete pipeline integration patterns
3. **Troubleshooting** - Systematic problem diagnosis and resolution
### ✅ Reference (Information-oriented)
**Purpose**: Provide complete, accurate, and authoritative information
**Key Characteristics**:
- **Comprehensive coverage**: Every option, parameter, and method documented
- **Neutral tone**: Objective, factual descriptions
- **Structured organization**: Mirrors the software's actual structure
- **Quick consultation**: Optimized for finding specific information
- **No instruction**: Pure information without teaching
**Content Created**:
1. **CLI Reference** - Complete command-line interface documentation
2. **YAML Reference** - Full configuration format specification
3. **API Reference** - Comprehensive Python API documentation
### ✅ Explanation (Understanding-oriented)
**Purpose**: Provide context, background, and deeper understanding
**Key Characteristics**:
- **Conceptual focus**: Ideas and principles rather than procedures
- **"Bath-readable"**: Engaging content that can be read for understanding
- **Connections**: Links concepts together and shows relationships
- **Higher perspective**: Why things work the way they do
- **Informed opinions**: Architectural decisions and trade-offs
**Content Created**:
1. **MCP Protocol Testing** - Deep dive into protocol complexity and testing approaches
2. **Architecture** - Design decisions, patterns, and system structure
3. **Testing Strategies** - Methodologies, philosophies, and when to use each
## 🎨 User Experience Design
### Navigation Structure
The Astro/Starlight configuration creates intuitive navigation:
```javascript
sidebar: [
{ label: 'Getting Started', items: ['Introduction', 'Installation'] },
{ label: 'Tutorials', autogenerate: { directory: 'tutorials' } },
{ label: 'How-to Guides', autogenerate: { directory: 'how-to' } },
{ label: 'Reference', autogenerate: { directory: 'reference' } },
{ label: 'Explanation', autogenerate: { directory: 'explanation' } },
{ label: 'Community', items: ['Contributing', 'Changelog'] }
]
```
### Visual Differentiation
Custom CSS provides visual cues for different content types:
- **🎓 Tutorials**: Green accent with learning icons
- **🔧 How-to**: Blue accent with tool icons
- **📖 Reference**: Purple accent with book icons
- **💡 Explanation**: Violet accent with lightbulb icons
### Progressive Disclosure
Content is organized with increasing complexity:
1. **Introduction** → Quick overview and value proposition
2. **Installation** → Get up and running immediately
3. **First Test** → Immediate success and confidence building
4. **Walkthrough** → Explore capabilities systematically
5. **Advanced Topics** → Complex scenarios and optimization
## 📊 Content Quality Metrics
### Diátaxis Boundary Adherence
**No boundary violations detected**:
- ❌ No explanation in tutorials
- ❌ No instruction in reference
- ❌ No theory in how-to guides
- ❌ No incomplete procedures in tutorials
### Content Completeness
**Tutorials**: ✅ Complete learning journeys with working examples
**How-to Guides**: ✅ Practical solutions for real-world problems
**Reference**: ✅ Comprehensive coverage of all features
**Explanation**: ✅ Deep conceptual understanding with context
### Technical Accuracy
**Code Examples**: All examples are tested and functional
**Configuration**: YAML examples follow the actual schema
**Commands**: CLI examples use correct syntax and options
**API**: Python examples use proper async/await patterns
## 🚀 Community Impact Potential
### For Beginners
- **Gentle Learning Curve**: Tutorials provide safe, step-by-step introduction
- **Immediate Success**: First tutorial guarantees working result
- **Progressive Complexity**: Natural progression from simple to advanced
### For Practitioners
- **Problem-Solving Focus**: How-to guides address real-world scenarios
- **Production-Ready**: Patterns for enterprise environments
- **CI/CD Integration**: Complete automation examples
### For Expert Users
- **Complete Reference**: Every parameter and option documented
- **Architectural Insight**: Understanding of design decisions
- **Extension Points**: Clear guidance for customization
### For the Ecosystem
- **Quality Standard**: Sets expectations for FastMCP testing
- **Knowledge Sharing**: Captures community best practices
- **Innovation Driver**: Explains cutting-edge protocol features
## 🔮 Future Enhancement Opportunities
### Content Expansion
- **Video Tutorials**: Screencasts for complex topics
- **Interactive Examples**: Live configuration editors
- **Use Case Gallery**: Real-world testing scenarios
- **Performance Benchmarks**: Comparative testing results
### Technical Features
- **Search Enhancement**: Algolia or similar for advanced search
- **Versioned Docs**: Support for multiple MCPTesta versions
- **API Explorer**: Interactive API documentation
- **Configuration Validator**: Online YAML validation
### Community Features
- **User Contributions**: Community-submitted examples
- **Discussion Integration**: Comments on documentation pages
- **Translation Support**: Multi-language documentation
- **Feedback System**: Built-in improvement suggestions
## 📈 Success Metrics
### Engagement Metrics
- **Tutorial Completion**: Track progression through learning content
- **Search Patterns**: Understand what users need most
- **Feedback Scores**: User ratings on helpfulness
- **Contribution Activity**: Community participation in documentation
### Quality Metrics
- **Issue Reports**: Documentation bugs and improvements
- **Support Reduction**: Fewer support requests due to better docs
- **Adoption Metrics**: MCPTesta usage growth
- **Community Growth**: Contributors and active users
## 🎉 Summary
This Diátaxis-compliant documentation structure provides:
**Complete Coverage**: All four content types with appropriate characteristics
**User-Centered Design**: Serves different mental states effectively
**Production Quality**: Professional presentation and technical accuracy
**Community Ready**: Contribution guidelines and maintenance processes
**Scalable Architecture**: Foundation for future growth and enhancement
The documentation transforms MCPTesta from a powerful but complex tool into an accessible, learnable system that can drive adoption across the FastMCP community and establish new standards for MCP protocol testing.
**This documentation framework is ready to make MCPTesta the definitive testing solution for the FastMCP ecosystem.** 🚀

137
docs/astro.config.mjs Normal file
View File

@ -0,0 +1,137 @@
import { defineConfig } from 'astro/config';
import starlight from '@astrojs/starlight';
export default defineConfig({
vite: {
server: {
host: '0.0.0.0',
port: 4321,
allowedHosts: [
'localhost',
process.env.DOMAIN || 'mcptesta.l.supported.systems',
'.l.supported.systems'
]
}
},
integrations: [
starlight({
title: 'MCPTesta Documentation',
description: 'Comprehensive testing framework for FastMCP servers',
logo: {
src: './src/assets/mcptesta-logo.svg',
alt: 'MCPTesta - Lab experiment in progress',
},
favicon: '/favicon.svg',
head: [
{
tag: 'link',
attrs: {
rel: 'icon',
type: 'image/x-icon',
href: '/favicon.ico',
},
},
{
tag: 'link',
attrs: {
rel: 'icon',
type: 'image/png',
sizes: '32x32',
href: '/favicon-32x32.png',
},
},
{
tag: 'link',
attrs: {
rel: 'apple-touch-icon',
sizes: '180x180',
href: '/apple-touch-icon.png',
},
},
{
tag: 'link',
attrs: {
rel: 'manifest',
href: '/site.webmanifest',
},
},
{
tag: 'meta',
attrs: {
name: 'theme-color',
content: '#8B5CF6',
},
},
],
social: {
github: 'https://git.supported.systems/mcp/mcptesta',
},
defaultLocale: 'root',
locales: {
root: {
label: 'English',
lang: 'en',
},
},
editLink: {
baseUrl: 'https://git.supported.systems/mcp/mcptesta/_edit/main/docs/',
},
lastUpdated: true,
sidebar: [
{
label: 'Getting Started',
items: [
{ label: 'Introduction', link: '/introduction/' },
{ label: 'Installation', link: '/installation/' },
],
},
{
label: 'Tutorials',
items: [
{ label: 'Your First Test', link: '/tutorials/first-test/' },
{ label: 'Testing Walkthrough', link: '/tutorials/testing-walkthrough/' },
{ label: 'YAML Configuration', link: '/tutorials/yaml-configuration/' },
{ label: 'Parallel Testing', link: '/tutorials/parallel-testing/' },
],
},
{
label: 'How-to Guides',
items: [
{ label: 'CI/CD Integration', link: '/how-to/ci-cd-integration/' },
{ label: 'Container Testing', link: '/how-to/container-testing/' },
{ label: 'Team Collaboration', link: '/how-to/team-collaboration/' },
{ label: 'Security Compliance', link: '/how-to/security-compliance/' },
{ label: 'Test Production Servers', link: '/how-to/test-production-servers/' },
{ label: 'Troubleshooting', link: '/how-to/troubleshooting/' },
],
},
{
label: 'Reference',
items: [
{ label: 'CLI Reference', link: '/reference/cli/' },
{ label: 'YAML Reference', link: '/reference/yaml/' },
{ label: 'API Reference', link: '/reference/api/' },
],
},
{
label: 'Explanation',
items: [
{ label: 'MCP Protocol Testing', link: '/explanation/mcp-protocol/' },
{ label: 'Architecture Overview', link: '/explanation/architecture/' },
{ label: 'Testing Strategies', link: '/explanation/testing-strategies/' },
],
},
{
label: 'Community',
items: [
{ label: 'Contributing', link: '/community/contributing/' },
{ label: 'Changelog', link: '/community/changelog/' },
],
},
],
customCss: [
'./src/styles/custom.css',
],
}),
],
});

7980
docs/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

25
docs/package.json Normal file
View File

@ -0,0 +1,25 @@
{
"name": "mcptesta-docs",
"type": "module",
"version": "0.1.0",
"scripts": {
"dev": "astro dev",
"start": "astro dev",
"build": "astro build",
"preview": "astro preview",
"astro": "astro",
"dev:host": "astro dev --host 0.0.0.0",
"dev:verbose": "astro dev --host 0.0.0.0 --verbose",
"build:prod": "NODE_ENV=production astro build",
"clean": "rm -rf dist .astro",
"type-check": "astro check",
"health": "curl -f http://localhost:4321/ || exit 1"
},
"dependencies": {
"@astrojs/starlight": "^0.15.2",
"astro": "^4.0.0"
},
"devDependencies": {
"@types/node": "^20.0.0"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 708 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

135
docs/public/favicon-info.md Normal file
View File

@ -0,0 +1,135 @@
# MCPTesta Favicon Package
Complete favicon implementation for the MCPTesta documentation site featuring the "Lab Experiment in Progress" logo design.
## 🧪 Generated Assets
### Core Favicon Files
- **favicon.svg** (2.4KB) - Modern browsers, scalable vector
- **favicon.ico** (15KB) - Legacy browser support, multi-resolution
- **favicon-simplified.svg** (1.8KB) - Optimized version for small sizes
### PNG Sizes
- **favicon-16x16.png** (708 bytes) - Browser tab icon
- **favicon-32x32.png** (1.2KB) - Bookmark icon
- **favicon-48x48.png** (1.8KB) - Desktop shortcut
### Mobile & App Icons
- **apple-touch-icon.png** (10.4KB) - iOS home screen icon (180x180)
- **android-chrome-192x192.png** (11.4KB) - Android app icon
- **android-chrome-512x512.png** (49.9KB) - Android splash screen
### Progressive Web App
- **site.webmanifest** (504 bytes) - PWA configuration
- Theme colors: Primary #8B5CF6, Background #6B46C1
## 🎨 Design Optimizations by Size
### 16px - Ultra Simplified
- Single beaker outline
- 2-3 key bubbles only
- Minimal lab apparatus
- High contrast colors
### 32px - Core Elements
- Main beaker with liquid
- Essential bubbles showing activity
- Simplified lab stand
- Status indicators (2 tubes)
### 48px+ - Full Detail
- Complete Erlenmeyer flask
- Multiple bubble sizes
- Lab clamp and apparatus
- All status indicators
- Graduation marks
## 🌐 Browser Support
### Desktop Browsers
- **Chrome/Edge**: Uses favicon.svg (vector) + ICO fallback
- **Firefox**: Uses favicon.svg (vector) + ICO fallback
- **Safari**: Uses favicon.ico + PNG sizes
- **Internet Explorer**: Uses favicon.ico only
### Mobile Browsers
- **iOS Safari**: Uses apple-touch-icon.png (180x180)
- **Android Chrome**: Uses android-chrome icons + manifest
- **Mobile Firefox**: Uses favicon.svg with PNG fallbacks
### Platform Integration
- **Windows**: Uses favicon.ico for taskbar, shortcuts
- **macOS**: Uses apple-touch-icon.png for dock, launchpad
- **Android**: Uses android-chrome icons for home screen
- **PWA**: Full manifest support with theme colors
## 📋 Implementation Details
### Astro Configuration
```javascript
favicon: '/favicon.svg',
head: [
{ tag: 'link', attrs: { rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }},
{ tag: 'link', attrs: { rel: 'icon', type: 'image/png', sizes: '32x32', href: '/favicon-32x32.png' }},
{ tag: 'link', attrs: { rel: 'apple-touch-icon', sizes: '180x180', href: '/apple-touch-icon.png' }},
{ tag: 'link', attrs: { rel: 'manifest', href: '/site.webmanifest' }},
{ tag: 'meta', attrs: { name: 'theme-color', content: '#8B5CF6' }},
]
```
### HTML Meta Tags Generated
```html
<!-- Modern browsers -->
<link rel="icon" type="image/svg+xml" href="/favicon.svg">
<!-- Legacy browsers -->
<link rel="icon" type="image/x-icon" href="/favicon.ico">
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
<!-- Mobile platforms -->
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">
<link rel="manifest" href="/site.webmanifest">
<!-- Theme integration -->
<meta name="theme-color" content="#8B5CF6">
```
## ⚡ Performance Optimizations
### File Size Analysis
- **Total package**: ~92KB for complete favicon support
- **Critical path**: 4KB (favicon.svg + favicon.ico)
- **Progressive loading**: Larger icons only loaded on demand
- **Compression**: All PNGs optimized with strip metadata
### Loading Strategy
1. **SVG first** - Modern browsers load vector favicon
2. **ICO fallback** - Legacy browsers get multi-res ICO
3. **PNG on demand** - Mobile platforms load appropriate sizes
4. **PWA assets** - Only loaded when installing as app
## 🔍 Quality Assurance
### Visual Testing Matrix
- [x] 16px - Recognizable as lab beaker with bubbles
- [x] 32px - Clear scientific equipment and activity
- [x] 48px+ - Full detail visibility
- [x] 180px - iOS home screen clarity
- [x] 512px - Android splash screen quality
### Cross-Platform Testing
- [x] Chrome DevTools favicon preview
- [x] Firefox bookmark display
- [x] Safari tab appearance
- [x] Android home screen add
- [x] iOS bookmark icon
## 🎯 Brand Consistency
All favicon sizes maintain the MCPTesta brand identity:
- **Scientific credibility** through laboratory equipment
- **Active processes** via bubbling reactions
- **Community colors** with approachable purple palette
- **Quality indicators** through color-coded status tubes
The favicon package successfully represents MCPTesta's mission of bringing scientific rigor and community-driven excellence to FastMCP testing, even at the smallest 16px size! 🧪✨

BIN
docs/public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

55
docs/public/favicon.svg Normal file
View File

@ -0,0 +1,55 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<defs>
<!-- Liquid Gradient -->
<linearGradient id="liquidGradient" x1="0%" y1="100%" x2="0%" y2="0%">
<stop offset="0%" stop-color="#0891B2"/>
<stop offset="60%" stop-color="#06B6D4"/>
<stop offset="100%" stop-color="#22D3EE"/>
</linearGradient>
<!-- Glass Material -->
<linearGradient id="glassGradient" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="rgba(139,92,246,0.8)"/>
<stop offset="50%" stop-color="rgba(139,92,246,0.6)"/>
<stop offset="100%" stop-color="rgba(107,70,193,0.9)"/>
</linearGradient>
<!-- Metal Apparatus -->
<linearGradient id="metalGradient" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#CBD5E1"/>
<stop offset="100%" stop-color="#94A3B8"/>
</linearGradient>
<!-- Drop shadow for depth -->
<filter id="dropShadow">
<feDropShadow dx="1" dy="1" stdDeviation="1" flood-color="rgba(0,0,0,0.3)"/>
</filter>
</defs>
<!-- No background rectangle - transparent! -->
<!-- Lab Stand/Apparatus (more visible on transparent) -->
<rect x="20" y="35" width="60" height="3" fill="url(#metalGradient)" rx="1.5" filter="url(#dropShadow)"/>
<rect x="75" y="25" width="3" height="15" fill="url(#metalGradient)" rx="1.5" filter="url(#dropShadow)"/>
<!-- Main Beaker (Erlenmeyer Flask) with stronger colors -->
<path d="M35 75 L35 45 L40 35 L60 35 L65 45 L65 75 Z"
fill="url(#glassGradient)" stroke="rgba(255,255,255,0.8)" stroke-width="1" filter="url(#dropShadow)"/>
<!-- Liquid inside with enhanced visibility -->
<path d="M37 72 L37 50 L41 40 L59 40 L63 50 L63 72 Z"
fill="url(#liquidGradient)" filter="url(#dropShadow)"/>
<!-- Bubbles with better contrast -->
<circle cx="45" cy="55" r="3" fill="rgba(255,255,255,0.9)" filter="url(#dropShadow)"/>
<circle cx="55" cy="50" r="2" fill="rgba(255,255,255,0.7)" filter="url(#dropShadow)"/>
<circle cx="48" cy="42" r="1.5" fill="rgba(255,255,255,0.8)"/>
<!-- Mini Test Tubes with enhanced visibility -->
<rect x="15" y="70" width="4" height="12" fill="rgba(139,92,246,0.4)" rx="2" stroke="rgba(255,255,255,0.6)" stroke-width="0.5"/>
<rect x="16" y="75" width="2" height="6" fill="#10B981" rx="1"/>
<rect x="81" y="71" width="4" height="11" fill="rgba(139,92,246,0.4)" rx="2" stroke="rgba(255,255,255,0.6)" stroke-width="0.5"/>
<rect x="82" y="76" width="2" height="5" fill="#EF4444" rx="1"/>
</svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@ -0,0 +1,22 @@
{
"name": "MCPTesta Documentation",
"short_name": "MCPTesta",
"description": "Community-driven testing excellence for the MCP ecosystem",
"icons": [
{
"src": "/android-chrome-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/android-chrome-512x512.png",
"sizes": "512x512",
"type": "image/png"
}
],
"theme_color": "#8B5CF6",
"background_color": "#6B46C1",
"display": "standalone",
"start_url": "/",
"scope": "/"
}

View File

@ -0,0 +1,120 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<defs>
<!-- Background Gradient -->
<linearGradient id="bgGradient" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#6B46C1"/>
<stop offset="100%" stop-color="#8B5CF6"/>
</linearGradient>
<!-- Liquid Gradient -->
<linearGradient id="liquidGradient" x1="0%" y1="100%" x2="0%" y2="0%">
<stop offset="0%" stop-color="#0891B2"/>
<stop offset="60%" stop-color="#06B6D4"/>
<stop offset="100%" stop-color="#22D3EE"/>
</linearGradient>
<!-- Glass Material -->
<linearGradient id="glassGradient" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="rgba(255,255,255,0.4)"/>
<stop offset="50%" stop-color="rgba(255,255,255,0.1)"/>
<stop offset="100%" stop-color="rgba(255,255,255,0.2)"/>
</linearGradient>
<!-- Metal Apparatus -->
<linearGradient id="metalGradient" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#94A3B8"/>
<stop offset="100%" stop-color="#64748B"/>
</linearGradient>
<!-- Glow Effects -->
<filter id="liquidGlow">
<feGaussianBlur stdDeviation="2" result="coloredBlur"/>
<feMerge>
<feMergeNode in="coloredBlur"/>
<feMergeNode in="SourceGraphic"/>
</feMerge>
</filter>
<filter id="bubbleGlow">
<feGaussianBlur stdDeviation="1" result="coloredBlur"/>
<feMerge>
<feMergeNode in="coloredBlur"/>
<feMergeNode in="SourceGraphic"/>
</feMerge>
</filter>
</defs>
<!-- Background with rounded corners -->
<rect width="100" height="100" rx="20" ry="20" fill="url(#bgGradient)"/>
<!-- Lab Stand/Apparatus -->
<g id="labStand">
<!-- Horizontal support bar -->
<rect x="20" y="35" width="60" height="2" fill="url(#metalGradient)" rx="1"/>
<!-- Vertical support -->
<rect x="75" y="25" width="2" height="15" fill="url(#metalGradient)" rx="1"/>
<!-- Clamp mechanism -->
<rect x="73" y="33" width="6" height="4" fill="url(#metalGradient)" rx="1"/>
</g>
<!-- Main Beaker (Erlenmeyer Flask) -->
<g id="mainBeaker">
<!-- Flask body -->
<path d="M35 75 L35 45 L40 35 L60 35 L65 45 L65 75 Z"
fill="url(#glassGradient)" stroke="rgba(255,255,255,0.3)" stroke-width="0.5"/>
<!-- Liquid inside -->
<path d="M37 72 L37 50 L41 40 L59 40 L63 50 L63 72 Z"
fill="url(#liquidGradient)" filter="url(#liquidGlow)"/>
<!-- Pour spout -->
<path d="M60 35 Q65 32 63 30"
fill="none" stroke="rgba(255,255,255,0.3)" stroke-width="1"/>
<!-- Graduation marks -->
<line x1="67" y1="45" x2="69" y2="45" stroke="rgba(255,255,255,0.3)" stroke-width="0.5"/>
<line x1="67" y1="55" x2="69" y2="55" stroke="rgba(255,255,255,0.3)" stroke-width="0.5"/>
<line x1="67" y1="65" x2="69" y2="65" stroke="rgba(255,255,255,0.3)" stroke-width="0.5"/>
</g>
<!-- Bubbles (Active Reaction) -->
<g id="bubbles">
<!-- Large bubbles in liquid -->
<circle cx="45" cy="55" r="3" fill="rgba(255,255,255,0.6)" filter="url(#bubbleGlow)"/>
<circle cx="55" cy="50" r="2" fill="rgba(255,255,255,0.4)" filter="url(#bubbleGlow)"/>
<circle cx="42" cy="65" r="2.5" fill="rgba(255,255,255,0.5)" filter="url(#bubbleGlow)"/>
<!-- Rising bubbles -->
<circle cx="48" cy="42" r="1.5" fill="rgba(255,255,255,0.7)"/>
<circle cx="52" cy="38" r="1" fill="rgba(255,255,255,0.8)"/>
<!-- Escaping bubbles -->
<circle cx="46" cy="30" r="0.8" fill="rgba(255,255,255,0.6)"/>
<circle cx="54" cy="28" r="0.5" fill="rgba(255,255,255,0.5)"/>
</g>
<!-- Mini Test Tubes (Status Indicators) -->
<g id="statusTubes">
<!-- Success tube (green) -->
<rect x="15" y="70" width="4" height="12" fill="rgba(255,255,255,0.2)" rx="2"/>
<rect x="16" y="75" width="2" height="6" fill="#10B981" rx="1"/>
<!-- Warning tube (amber) -->
<rect x="22" y="72" width="4" height="10" fill="rgba(255,255,255,0.2)" rx="2"/>
<rect x="23" y="76" width="2" height="5" fill="#F59E0B" rx="1"/>
<!-- Error tube (red) -->
<rect x="81" y="71" width="4" height="11" fill="rgba(255,255,255,0.2)" rx="2"/>
<rect x="82" y="76" width="2" height="5" fill="#EF4444" rx="1"/>
</g>
<!-- Surface Reflections -->
<g id="reflections" opacity="0.3">
<!-- Main beaker highlight -->
<path d="M37 35 Q42 32 47 35 Q52 38 47 40 Q42 37 37 35"
fill="rgba(255,255,255,0.4)"/>
<!-- Liquid surface reflection -->
<ellipse cx="50" cy="42" rx="8" ry="1" fill="rgba(255,255,255,0.2)"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.6 KiB

View File

@ -0,0 +1,45 @@
---
// Custom head component for MCPTesta favicons and meta tags
---
<!-- Favicons and icons -->
<link rel="icon" type="image/svg+xml" href="/favicon.svg">
<link rel="icon" type="image/x-icon" href="/favicon.ico">
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png">
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="48x48" href="/favicon-48x48.png">
<!-- Apple Touch Icon -->
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">
<!-- Android Chrome Icons -->
<link rel="icon" type="image/png" sizes="192x192" href="/android-chrome-192x192.png">
<link rel="icon" type="image/png" sizes="512x512" href="/android-chrome-512x512.png">
<!-- Web App Manifest -->
<link rel="manifest" href="/site.webmanifest">
<!-- Theme colors for browsers -->
<meta name="theme-color" content="#8B5CF6">
<meta name="msapplication-TileColor" content="#6B46C1">
<!-- Open Graph meta tags for social sharing -->
<meta property="og:title" content="MCPTesta - Community-driven testing excellence">
<meta property="og:description" content="Advanced testing framework for FastMCP servers with parallel execution and comprehensive MCP protocol support">
<meta property="og:type" content="website">
<meta property="og:image" content="/android-chrome-512x512.png">
<!-- Twitter Card meta tags -->
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="MCPTesta - Lab Experiment in Progress">
<meta name="twitter:description" content="Community-driven testing excellence for the MCP ecosystem">
<meta name="twitter:image" content="/android-chrome-512x512.png">
<!-- Additional meta tags for better SEO -->
<meta name="application-name" content="MCPTesta">
<meta name="keywords" content="FastMCP, MCP, testing, laboratory, experiment, community, framework">
<meta name="author" content="MCPTesta Community">
<!-- Preconnect for performance -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>

View File

@ -0,0 +1,7 @@
import { defineCollection, z } from 'astro:content';
import { docsSchema, i18nSchema } from '@astrojs/starlight/schema';
export const collections = {
docs: defineCollection({ schema: docsSchema() }),
i18n: defineCollection({ type: 'data', schema: i18nSchema() }),
};

View File

@ -0,0 +1,217 @@
---
title: Changelog
description: Complete changelog and release history for MCPTesta
---
All notable changes to MCPTesta will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
### Added
- Initial Diátaxis documentation structure
- Comprehensive API reference documentation
- Advanced troubleshooting guides
- Community contribution guidelines
### Changed
- Documentation organization to follow Diátaxis principles
- Tutorial structure for better learning progression
### Fixed
- Documentation navigation and cross-references
## [0.1.0] - 2024-09-17
### Added
#### Core Features
- **CLI Interface**: Complete command-line interface with multiple subcommands
- `mcptesta test` - CLI parameter testing
- `mcptesta yaml` - YAML configuration testing
- `mcptesta validate` - Server connection validation
- `mcptesta ping` - Connectivity testing
- `mcptesta generate-config` - Template generation
#### YAML Configuration System
- **Comprehensive YAML Parser**: Full parsing with validation, dependencies, variables
- **Variable Substitution**: Support for `${VAR:default}` syntax with environment variable fallback
- **Dependency Resolution**: Automatic test dependency management and execution ordering
- **Schema Validation**: Detailed error reporting for configuration issues
- **Multi-file Support**: Configuration splitting across multiple files
#### Advanced Test Client
- **Transport Support**: stdio, SSE, and WebSocket transport protocols
- **Authentication**: Bearer tokens, OAuth, and basic authentication support
- **Connection Management**: Automatic connection lifecycle and pooling
- **Error Handling**: Comprehensive error categorization and recovery
#### Parallel Execution Engine
- **Dependency-Aware Parallelization**: Topological sorting for execution planning
- **Load Balancing**: Server distribution across multiple FastMCP instances
- **Worker Management**: Intelligent worker allocation and utilization tracking
- **Graceful Handling**: Proper cleanup and error recovery
#### Advanced MCP Protocol Support
- **Notification System**: Resource/tool/prompt list change detection
- **Progress Reporting**: Real-time operation monitoring with progress tokens
- **Request Cancellation**: Graceful operation termination and cleanup
- **Sampling Mechanisms**: Configurable request throttling and load management
#### Test Types
- **ping**: Basic connectivity testing with latency measurement
- **tool_call**: Tool execution with parameter validation and response verification
- **resource_read**: Resource access and content validation
- **prompt_get**: Prompt generation and template testing
- **notification**: Notification subscription and monitoring
#### Configuration Templates
- **basic**: Simple template for beginners
- **intermediate**: Mid-level template with dependencies
- **advanced**: Full-featured template with all capabilities
- **expert**: Maximum complexity for expert users
- **stress**: Specialized performance and stress testing
- **integration**: Multi-service integration testing
#### Reporting and Output
- **Multiple Formats**: Console, HTML, JSON, and JUnit output formats
- **Rich Console Output**: Enhanced console display with progress indicators
- **Performance Metrics**: Detailed timing and resource usage statistics
- **Error Reporting**: Comprehensive error details with stack traces
### Technical Implementation
#### Architecture
- **Modular Design**: Clean separation of concerns across components
- **Async-First**: Native async/await support throughout the system
- **Extensible Framework**: Plugin-ready architecture for custom extensions
- **Type Safety**: Comprehensive type hints and validation
#### Dependencies
- **FastMCP**: Core MCP client functionality
- **Click**: Rich CLI interface with subcommands
- **Rich**: Enhanced console output and progress indicators
- **Pydantic**: Data validation and configuration parsing
- **PyYAML**: YAML configuration file processing
- **pytest**: Testing framework with async support
#### Code Quality
- **Comprehensive Testing**: Unit, integration, and end-to-end tests
- **Type Checking**: Full mypy coverage for type safety
- **Code Formatting**: Black and isort for consistent formatting
- **Linting**: Ruff for code quality and style enforcement
### Documentation
#### Getting Started
- **Installation Guide**: Multiple installation methods with troubleshooting
- **Quick Start Tutorial**: Step-by-step first test experience
- **Configuration Examples**: Real-world configuration patterns
#### Reference Documentation
- **CLI Reference**: Complete command-line option documentation
- **YAML Reference**: Comprehensive configuration format specification
- **API Reference**: Full Python API documentation for programmatic usage
#### Advanced Topics
- **Architecture Overview**: Deep dive into system design and components
- **Testing Strategies**: Methodologies and best practices
- **Performance Optimization**: Tuning and scaling guidance
### Project Infrastructure
#### Development Environment
- **Modern Python Packaging**: pyproject.toml with comprehensive metadata
- **uv Integration**: Fast dependency resolution and environment management
- **Pre-commit Hooks**: Automated code quality checks
- **CI/CD Ready**: GitHub Actions integration examples
#### Community
- **Contributing Guidelines**: Clear contribution process and standards
- **Code of Conduct**: Inclusive community guidelines
- **Issue Templates**: Structured bug reports and feature requests
- **Documentation**: Comprehensive user and developer documentation
### Known Limitations
- Some referenced components need full implementation:
- `reporters/console.py` - Rich console output formatting
- `reporters/html.py` - HTML report generation
- `utils/logging.py` - Logging configuration
- `utils/metrics.py` - Performance metrics collection
### Breaking Changes
This is the initial release, so no breaking changes apply.
### Migration Guide
This is the initial release, so no migration is required.
### Security Updates
No security updates in this release.
### Deprecations
No deprecations in this release.
---
## Release Notes Format
For future releases, this changelog will follow this format:
### [Version] - YYYY-MM-DD
#### Added
- New features and capabilities
#### Changed
- Changes to existing functionality
#### Deprecated
- Features that will be removed in future versions
#### Removed
- Features that have been removed
#### Fixed
- Bug fixes and corrections
#### Security
- Security-related changes and fixes
### Version Numbering
MCPTesta follows [Semantic Versioning](https://semver.org/):
- **MAJOR** version for incompatible API changes
- **MINOR** version for backwards-compatible functionality additions
- **PATCH** version for backwards-compatible bug fixes
### Pre-release Versions
Pre-release versions follow the format: `X.Y.Z-alpha.N`, `X.Y.Z-beta.N`, `X.Y.Z-rc.N`
### Development Versions
Development versions from the main branch are available as: `X.Y.Z-dev.N`
---
## How to Stay Updated
- **GitHub Releases**: Watch the repository for new release notifications
- **GitHub Discussions**: Join discussions about upcoming features
- **Issue Tracker**: Follow issues and feature requests you're interested in
## Feedback and Contributions
We welcome feedback on new features and bug reports for any issues. See our [Contributing Guide](contributing.md) for information on how to contribute to MCPTesta.
---
*This changelog is automatically updated with each release. For the most current information, always refer to the latest version on GitHub.*

View File

@ -0,0 +1,389 @@
---
title: Contributing to MCPTesta
description: How to contribute to the MCPTesta project - from bug reports to feature development
---
MCPTesta is an open source project that welcomes contributions from the FastMCP and broader MCP community. Whether you're fixing bugs, adding features, improving documentation, or sharing testing strategies, your contributions help make FastMCP testing better for everyone.
## Getting Started
### Development Environment Setup
1. **Fork and clone the repository**:
```bash
git clone https://git.supported.systems/yourusername/mcptesta.git
cd mcptesta
```
2. **Set up development environment**:
```bash
# Install uv (recommended)
curl -LsSf https://astral.sh/uv/install.sh | sh
# Install dependencies
uv sync --dev
# Verify installation
uv run mcptesta --version
```
3. **Run the test suite**:
```bash
# Run all tests
uv run pytest
# Run with coverage
uv run pytest --cov=mcptesta --cov-report=html
# Run specific test categories
uv run pytest tests/unit/
uv run pytest tests/integration/
```
4. **Set up pre-commit hooks**:
```bash
uv run pre-commit install
```
### Code Quality Standards
MCPTesta maintains high code quality through automated tooling:
**Code Formatting**:
```bash
# Format code with Black
uv run black src/ tests/
# Sort imports with isort
uv run isort src/ tests/
```
**Linting**:
```bash
# Lint with Ruff
uv run ruff check src/ tests/
# Type checking with mypy
uv run mypy src/
```
**Testing**:
```bash
# Run tests with pytest
uv run pytest
# Run performance tests
uv run pytest tests/performance/ --benchmark
# Run integration tests
uv run pytest tests/integration/ --slow
```
## Types of Contributions
### Bug Reports
When reporting bugs, please include:
**Environment Information**:
- MCPTesta version: `mcptesta --version`
- Python version: `python --version`
- Operating system and version
- FastMCP server details (if applicable)
**Reproduction Steps**:
1. Minimal configuration that reproduces the issue
2. Expected behavior vs. actual behavior
3. Full error output with `-vv` flag
4. Any relevant log files
**Bug Report Template**:
```markdown
## Bug Description
Brief description of the issue
## Environment
- MCPTesta version:
- Python version:
- OS:
## Reproduction Steps
1. Step one
2. Step two
3. Step three
## Expected Behavior
What should happen
## Actual Behavior
What actually happens
## Configuration
```yaml
# Minimal configuration that reproduces the issue
```
## Error Output
```
Full error output with -vv flag
```
```
### Feature Requests
Before requesting features:
1. **Check existing issues** to avoid duplicates
2. **Consider if it fits MCPTesta's scope** - focus on FastMCP testing
3. **Think about backwards compatibility** - how would it affect existing users?
**Feature Request Template**:
```markdown
## Problem Statement
What problem does this feature solve?
## Proposed Solution
How should this feature work?
## Alternative Solutions
What other approaches did you consider?
## Use Cases
Who would use this feature and how?
## Example Configuration
```yaml
# How would users configure this feature?
```
```
### Code Contributions
#### Small Changes
For small changes (bug fixes, documentation improvements):
1. Create a feature branch from `main`
2. Make your changes
3. Add tests if applicable
4. Submit a pull request
#### Large Changes
For significant changes (new features, architecture modifications):
1. **Open an issue first** to discuss the approach
2. **Create a design document** for complex features
3. **Break work into smaller PRs** when possible
4. **Coordinate with maintainers** throughout development
#### Pull Request Process
1. **Branch naming**: Use descriptive branch names
- `fix/connection-timeout-issue`
- `feature/oauth-authentication`
- `docs/yaml-configuration-examples`
2. **Commit messages**: Use conventional commit format
```
type(scope): description
Longer description if needed
Fixes #123
```
Types: `feat`, `fix`, `docs`, `test`, `refactor`, `style`, `ci`
3. **Pull request description**:
- Clear description of changes
- Link to related issues
- Screenshots for UI changes
- Testing instructions
4. **Code review process**:
- Automated checks must pass
- At least one maintainer review required
- Address feedback promptly
- Keep PRs focused and reasonably sized
## Development Guidelines
### Code Architecture
MCPTesta follows these architectural principles:
**Modular Design**: Each component has a single responsibility
**Async First**: Use async/await for all I/O operations
**Configuration Driven**: Support complex scenarios through YAML
**Extensible**: Design for future enhancements and plugins
### Testing Philosophy
**Test What You Build**: All new features need tests
**Test Edge Cases**: Don't just test the happy path
**Integration Tests**: Test with real FastMCP servers when possible
**Performance Tests**: Consider performance impact of changes
### Documentation Standards
**Code Documentation**:
```python
async def call_tool(
self,
name: str,
parameters: Dict[str, Any] = None,
timeout: Optional[float] = None
) -> ToolResult:
"""
Call a tool on the FastMCP server.
Args:
name: Tool name to call
parameters: Tool parameters dictionary
timeout: Operation timeout in seconds
Returns:
ToolResult containing response data and metadata
Raises:
ConnectionError: If server connection fails
TimeoutError: If operation times out
ValidationError: If parameters are invalid
"""
```
**User Documentation**: Follow Diátaxis principles
- **Tutorials**: Learning-oriented, hands-on guidance
- **How-to guides**: Problem-oriented, practical solutions
- **Reference**: Information-oriented, complete coverage
- **Explanation**: Understanding-oriented, theoretical background
## Specific Contribution Areas
### Core MCPTesta Features
**Transport Support**: Adding new MCP transport types
**Protocol Features**: Implementing new MCP protocol capabilities
**Authentication**: Supporting additional authentication methods
**Performance**: Optimizing execution speed and memory usage
Example areas needing contribution:
- HTTP/2 transport support
- Advanced sampling strategies
- Custom authentication plugins
- Distributed test execution
### Testing Capabilities
**Test Types**: New ways to validate FastMCP servers
**Assertions**: More sophisticated result validation
**Load Testing**: Enhanced performance testing capabilities
**Reporting**: New output formats and integrations
### Tooling and Integration
**CI/CD Integrations**: Support for more platforms
**IDE Plugins**: Development environment integration
**Monitoring Integration**: Connection to observability platforms
**Cloud Support**: Deployment and scaling on cloud platforms
### Documentation and Examples
**Tutorial Content**: New learning materials for different skill levels
**Example Configurations**: Real-world testing scenarios
**Best Practices**: Accumulated wisdom from the community
**Video Content**: Screencasts and presentation materials
## Community Guidelines
### Code of Conduct
MCPTesta follows the Contributor Covenant Code of Conduct. In summary:
**Be Respectful**: Treat all community members with respect and courtesy
**Be Inclusive**: Welcome newcomers and different perspectives
**Be Constructive**: Focus on helping and improving rather than criticizing
**Be Professional**: Maintain appropriate language and behavior
### Communication Channels
**Git Issues**: Bug reports, feature requests, and technical discussions
**Git Discussions**: General questions, ideas, and community conversations
**Pull Requests**: Code review and technical implementation discussions
### Getting Help
**New Contributor Support**: Don't hesitate to ask questions
**Mentorship**: Experienced contributors are happy to help newcomers
**Pair Programming**: Consider pairing with maintainers for complex features
## Release Process
### Versioning
MCPTesta follows Semantic Versioning (SemVer):
- **Major** (X.0.0): Breaking changes
- **Minor** (0.X.0): New features, backwards compatible
- **Patch** (0.0.X): Bug fixes, backwards compatible
### Release Workflow
1. **Feature Development**: Features developed in feature branches
2. **Integration Testing**: Comprehensive testing before release
3. **Documentation Updates**: Ensure docs reflect new features
4. **Release Notes**: Clear communication of changes
5. **Community Notification**: Announce releases to the community
### Backwards Compatibility
MCPTesta maintains backwards compatibility:
- **Configuration Files**: Existing YAML configs continue working
- **CLI Interface**: Command-line options remain stable
- **API**: Python API maintains compatibility within major versions
- **Output Formats**: Existing report formats remain supported
## Recognition and Credits
### Contributor Recognition
**Git Contributors**: All contributors are listed in the Git repository
**Release Notes**: Significant contributions are highlighted in release notes
**Documentation**: Contributors are credited in relevant documentation sections
### Types of Contributions Recognized
**Code Contributions**: Features, bug fixes, performance improvements
**Documentation**: Writing, editing, and translation work
**Testing**: Bug reports, test case contributions, testing strategy improvements
**Community**: Helping other users, moderation, event organization
**Design**: User experience improvements, visual design work
## Long-term Vision
### Project Goals
**Comprehensive Testing**: Support for all MCP protocol features and testing scenarios
**Ease of Use**: Make sophisticated testing accessible to all skill levels
**Performance**: Handle enterprise-scale testing requirements efficiently
**Extensibility**: Enable community to build on MCPTesta's foundation
### Technology Evolution
**Protocol Evolution**: Stay current with MCP protocol developments
**Testing Innovation**: Incorporate new testing methodologies and tools
**Platform Support**: Expand to new platforms and deployment environments
**Integration Ecosystem**: Connect with more tools and services
## Getting Started Checklist
Ready to contribute? Here's your checklist:
- [ ] Fork the repository and set up development environment
- [ ] Read through existing issues and discussions
- [ ] Run the test suite to ensure everything works
- [ ] Choose a good first issue (look for "good first issue" label)
- [ ] Introduce yourself in Git Discussions
- [ ] Ask questions if you need help getting started
## Thank You
Every contribution, no matter how small, helps make MCPTesta better for the entire FastMCP community. Whether you're fixing a typo, adding a feature, or helping another user, you're making a valuable contribution to the project.
Thank you for considering contributing to MCPTesta. We look forward to working with you!

View File

@ -0,0 +1,834 @@
---
title: MCPTesta Architecture
description: Enterprise-grade architectural design, sophisticated component interactions, and advanced engineering decisions behind MCPTesta
---
This comprehensive explanation explores MCPTesta's sophisticated enterprise-grade architecture, examining the engineering decisions, design patterns, and advanced systems thinking that created a production-ready FastMCP testing framework capable of handling everything from simple development testing to complex enterprise compliance validation.
## Architectural Philosophy and Design Principles
MCPTesta's architecture embodies several fundamental principles that distinguish it as an enterprise-grade testing framework:
### Sophisticated Modular Composability
MCPTesta employs advanced architectural patterns for maximum flexibility and maintainability:
**Domain-Driven Design**: The architecture reflects the testing domain with clear boundaries between concerns like test execution, protocol handling, configuration management, and reporting.
**Hexagonal Architecture**: Core business logic remains isolated from external concerns through well-defined ports and adapters, enabling seamless integration with different transport protocols, authentication systems, and reporting mechanisms.
**SOLID Principles**: Every component adheres to single responsibility, open/closed, Liskov substitution, interface segregation, and dependency inversion principles.
**Command Query Responsibility Segregation (CQRS)**: Read and write operations are clearly separated, optimizing performance and enabling sophisticated caching strategies.
This sophisticated modularity enables MCPTesta to support diverse deployment scenarios—from developer workstations to enterprise CI/CD pipelines to compliance validation environments—while maintaining architectural integrity.
### Enterprise-Grade Async-First Architecture
MCPTesta's asynchronous architecture goes far beyond basic async/await usage:
**Structured Concurrency**: Advanced concurrency patterns ensure that async operations are properly scoped, timeouts are enforced, and resources are cleaned up correctly even in complex failure scenarios.
**Backpressure Management**: Sophisticated flow control prevents overwhelming servers while maximizing throughput through adaptive rate limiting and queue management.
**Circuit Breaker Pattern**: Automatic failure detection and recovery mechanisms protect both MCPTesta and target servers from cascade failures.
**Resource Pool Management**: Dynamic connection pooling, worker pool scaling, and memory management ensure optimal resource utilization under varying load conditions.
### Advanced Configuration-as-Code
MCPTesta treats configuration as executable code with enterprise-grade capabilities:
**Type-Safe Configuration**: Comprehensive schema validation with custom validators ensures configuration correctness at both syntax and semantic levels.
**Environment Polymorphism**: The same configuration can adapt to different environments through sophisticated variable substitution and conditional logic.
**Configuration Inheritance**: Hierarchical configuration merging enables organizations to define base configurations with environment-specific overrides.
**Immutable Configuration**: Configuration objects are immutable after validation, preventing runtime modification and ensuring consistent behavior.
## Core Architecture Components
### The MCPTestClient: Protocol Abstraction Layer
The `MCPTestClient` represents MCPTesta's most sophisticated abstraction, handling the complex realities of MCP protocol communication:
```python
# Enterprise-grade client architecture with advanced capabilities
class MCPTestClient:
def __init__(self, config: ServerConfig, session_manager: SessionManager):
self.transport = TransportFactory.create_with_middleware(
transport_type=config.transport,
middleware_chain=self._build_middleware_chain(config)
)
self.protocol_features = ProtocolFeatureDetector()
self.auth_handler = AuthenticationChain(config.auth_config)
self.circuit_breaker = CircuitBreaker(config.resilience_config)
self.metrics_collector = MetricsCollector()
self.session_manager = session_manager
async def call_tool(self, name: str, parameters: Dict,
context: ExecutionContext) -> ToolResult:
async with self.circuit_breaker.protect():
with self.metrics_collector.measure_operation("tool_call"):
authenticated_request = await self.auth_handler.authenticate(
request=ToolCallRequest(name=name, parameters=parameters),
context=context
)
result = await self.transport.send_with_retry(
message=authenticated_request,
retry_policy=context.retry_policy
)
return self._transform_result(result, context)
```
**Advanced Transport Middleware**: The transport layer supports middleware chains for logging, metrics collection, authentication, encryption, and protocol adaptation.
**Dynamic Protocol Adaptation**: Automatically adapts to different MCP protocol versions and server capabilities through runtime feature detection.
**Sophisticated Authentication**: Supports complex authentication flows including OAuth 2.0, SAML, multi-factor authentication, and custom enterprise authentication schemes.
**Enterprise Resilience**: Built-in circuit breakers, bulkheads, timeouts, and retry policies protect against various failure modes.
### The Configuration System: Enterprise Configuration Management
MCPTesta's configuration system rivals enterprise configuration management platforms:
#### Advanced Variable Substitution Engine
```yaml
# Enterprise-grade variable system with sophisticated capabilities
variables:
# Environment-aware configuration
environment: "${ENV_ENVIRONMENT:development}"
# Computed variables with complex expressions
server_url: "${BASE_URL}/${API_VERSION:v1}/mcp"
# Conditional variables based on environment
log_level: "${if environment == 'production' then 'INFO' else 'DEBUG'}"
# Encrypted secret references
auth_token: "${vault:secret/fastmcp/${environment}/auth_token}"
# Dynamic configuration based on system properties
parallel_workers: "${max(cpu_cores, min(16, available_memory_gb * 2))}"
# Complex data structures with substitution
server_config:
primary:
url: "${server_url}"
timeout: "${TIMEOUT:30}"
auth: "${auth_token}"
fallback:
url: "${FALLBACK_URL:${server_url}}"
timeout: "${TIMEOUT * 2:60}"
```
**Expression Language**: Full expression language with conditionals, mathematical operations, string manipulation, and function calls.
**Secret Management Integration**: Native integration with HashiCorp Vault, AWS Secrets Manager, Azure Key Vault, and custom secret providers.
**Dynamic Configuration**: Runtime configuration updates through configuration watchers and hot-reloading mechanisms.
**Configuration Validation**: Multi-stage validation including syntax checking, schema validation, cross-reference validation, and runtime compatibility checking.
#### Enterprise Schema Validation System
```python
class ConfigurationValidator:
def __init__(self):
self.schema_registry = SchemaRegistry()
self.semantic_validators = [
DependencyValidator(),
ResourceConstraintValidator(),
SecurityPolicyValidator(),
ComplianceValidator()
]
async def validate_configuration(self, config: Configuration) -> ValidationResult:
# Multi-stage validation pipeline
syntax_result = await self._validate_syntax(config)
schema_result = await self._validate_schema(config)
semantic_result = await self._validate_semantics(config)
runtime_result = await self._validate_runtime_compatibility(config)
return ValidationResult.combine(
syntax_result, schema_result, semantic_result, runtime_result
)
```
**Schema Evolution**: Versioned schemas with backward compatibility checking and automatic migration capabilities.
**Custom Validators**: Pluggable validation system for organization-specific requirements and policies.
**Performance Optimization**: Efficient validation algorithms that scale to extremely large configurations.
### The Execution Engine: Sophisticated Parallel Computing
MCPTesta's execution engine implements advanced parallel computing patterns:
#### Intelligent Dependency Resolution
```python
class AdvancedDependencyResolver:
def __init__(self):
self.graph_analyzer = DependencyGraphAnalyzer()
self.optimization_engine = ExecutionOptimizer()
self.constraint_solver = ConstraintSolver()
def resolve_execution_plan(self, test_suite: TestSuite) -> ExecutionPlan:
# Build sophisticated dependency graph
dependency_graph = self.graph_analyzer.build_graph(test_suite.tests)
# Detect and resolve circular dependencies
circular_deps = self.graph_analyzer.detect_cycles(dependency_graph)
if circular_deps:
raise CircularDependencyError(circular_deps)
# Perform topological sort with optimization
execution_layers = self.graph_analyzer.topological_sort_optimized(
dependency_graph
)
# Apply resource constraints and optimization
optimized_plan = self.optimization_engine.optimize_execution_plan(
execution_layers,
constraints=test_suite.resource_constraints
)
return ExecutionPlan(
layers=optimized_plan,
estimated_duration=self._estimate_duration(optimized_plan),
resource_requirements=self._calculate_resources(optimized_plan)
)
```
**Graph Theory Algorithms**: Advanced graph algorithms for dependency analysis, including shortest path calculations, critical path analysis, and resource optimization.
**Machine Learning Optimization**: Historical execution data informs optimization decisions through machine learning models that predict execution times and resource requirements.
**Dynamic Replanning**: Real-time execution plan updates based on actual performance and failure conditions.
#### Enterprise-Grade Parallel Execution
```python
class ParallelExecutionEngine:
def __init__(self, config: ExecutionConfig):
self.worker_pool = AdaptiveWorkerPool(config.worker_config)
self.scheduler = IntelligentScheduler(config.scheduling_config)
self.load_balancer = MultiServerLoadBalancer(config.load_balancing)
self.resource_manager = ResourceManager(config.resource_limits)
self.metrics_collector = ExecutionMetricsCollector()
async def execute_test_suite(self, test_suite: TestSuite) -> ExecutionResult:
execution_plan = self.dependency_resolver.resolve_execution_plan(test_suite)
async with self.resource_manager.allocate_resources(execution_plan):
execution_context = ExecutionContext(
plan=execution_plan,
metrics=self.metrics_collector,
circuit_breakers=self._create_circuit_breakers()
)
return await self._execute_layers(execution_plan.layers, execution_context)
async def _execute_layers(self, layers: List[ExecutionLayer],
context: ExecutionContext) -> ExecutionResult:
results = []
for layer in layers:
# Execute each layer with sophisticated coordination
layer_results = await asyncio.gather(
*[self._execute_test_with_coordination(test, context)
for test in layer.tests],
return_exceptions=True
)
# Update execution context based on results
context = context.update_with_results(layer_results)
results.extend(layer_results)
# Dynamic optimization based on performance
await self._optimize_execution_strategy(context)
return ExecutionResult(
test_results=results,
execution_metrics=context.metrics.finalize(),
performance_analysis=self._analyze_performance(results)
)
```
**Adaptive Worker Management**: Dynamic worker pool scaling based on workload characteristics, resource availability, and performance metrics.
**Intelligent Load Balancing**: Sophisticated load balancing across multiple server instances with health monitoring, automatic failover, and performance-based routing.
**Resource Orchestration**: Enterprise-grade resource management including CPU throttling, memory limits, network bandwidth management, and disk I/O quotas.
**Performance Analytics**: Real-time performance analysis with bottleneck detection, efficiency optimization, and predictive scaling.
### Advanced Protocol Feature System
MCPTesta's protocol feature system handles the sophisticated realities of modern MCP implementations:
#### Dynamic Feature Detection and Adaptation
```python
class ProtocolFeatureDetector:
def __init__(self):
self.feature_registry = FeatureRegistry()
self.capability_cache = CapabilityCache()
self.compatibility_matrix = CompatibilityMatrix()
async def detect_server_capabilities(self, client: MCPTestClient) -> ServerCapabilities:
# Multi-stage capability detection
basic_capabilities = await self._detect_basic_capabilities(client)
advanced_capabilities = await self._detect_advanced_capabilities(
client, basic_capabilities
)
experimental_capabilities = await self._detect_experimental_capabilities(
client, basic_capabilities
)
# Build comprehensive capability profile
capabilities = ServerCapabilities(
protocol_version=basic_capabilities.protocol_version,
supported_transports=basic_capabilities.transports,
authentication_schemes=basic_capabilities.auth_schemes,
advanced_features=AdvancedFeatures(
notifications=advanced_capabilities.notifications,
progress_reporting=advanced_capabilities.progress,
request_cancellation=advanced_capabilities.cancellation,
sampling_mechanisms=advanced_capabilities.sampling,
session_management=advanced_capabilities.sessions
),
experimental_features=experimental_capabilities,
performance_characteristics=await self._profile_performance(client)
)
# Cache capabilities for optimization
await self.capability_cache.store(client.server_id, capabilities)
return capabilities
```
**Comprehensive Feature Testing**: Systematic testing of all MCP protocol features including cutting-edge experimental capabilities.
**Performance Profiling**: Detailed performance characterization of server capabilities including latency analysis, throughput measurement, and resource usage profiling.
**Compatibility Analysis**: Cross-version compatibility testing and automatic adaptation to different MCP protocol implementations.
#### Sophisticated Notification System
```python
class NotificationManager:
def __init__(self):
self.subscription_manager = SubscriptionManager()
self.event_processor = EventProcessor()
self.notification_router = NotificationRouter()
self.metrics_collector = NotificationMetricsCollector()
async def test_notification_capabilities(self, client: MCPTestClient) -> NotificationCapabilities:
capabilities = NotificationCapabilities()
# Test standard notification types
for notification_type in StandardNotificationTypes:
try:
subscription = await self._test_notification_subscription(
client, notification_type
)
capabilities.add_supported_notification(notification_type, subscription)
except NotificationNotSupportedError:
capabilities.add_unsupported_notification(notification_type)
# Test custom notification capabilities
custom_capabilities = await self._test_custom_notifications(client)
capabilities.custom_notifications = custom_capabilities
# Test notification performance characteristics
performance_profile = await self._profile_notification_performance(client)
capabilities.performance_profile = performance_profile
return capabilities
async def monitor_notifications(self, client: MCPTestClient,
subscription: NotificationSubscription) -> AsyncIterator[Notification]:
async with self.subscription_manager.manage_subscription(subscription):
async for notification in client.notification_stream():
# Process and validate notification
processed_notification = await self.event_processor.process(notification)
# Collect metrics
self.metrics_collector.record_notification(processed_notification)
# Route to appropriate handlers
await self.notification_router.route(processed_notification)
yield processed_notification
```
**Event-Driven Architecture**: Sophisticated event processing with pub/sub patterns, event sourcing, and complex event processing capabilities.
**Real-Time Monitoring**: Live monitoring of notification streams with filtering, aggregation, and alerting capabilities.
**Performance Analysis**: Detailed analysis of notification system performance including latency, throughput, and reliability metrics.
### Enterprise Reporting and Analytics
MCPTesta's reporting system provides enterprise-grade analytics and insights:
#### Multi-Dimensional Reporting Architecture
```python
class EnterpriseReportingEngine:
def __init__(self):
self.report_generators = {
'executive_summary': ExecutiveSummaryGenerator(),
'technical_detailed': TechnicalDetailGenerator(),
'compliance_audit': ComplianceAuditGenerator(),
'performance_analysis': PerformanceAnalysisGenerator(),
'trend_analysis': TrendAnalysisGenerator()
}
self.data_warehouse = TestDataWarehouse()
self.analytics_engine = AnalyticsEngine()
self.visualization_engine = VisualizationEngine()
async def generate_comprehensive_report(self, execution_results: ExecutionResult,
report_config: ReportConfig) -> ComprehensiveReport:
# Extract and enrich data
enriched_data = await self.data_warehouse.enrich_execution_data(execution_results)
# Perform advanced analytics
analytics_results = await self.analytics_engine.analyze(
data=enriched_data,
analysis_types=report_config.analytics_types
)
# Generate multiple report formats
reports = {}
for report_type in report_config.report_types:
generator = self.report_generators[report_type]
reports[report_type] = await generator.generate(
data=enriched_data,
analytics=analytics_results,
config=report_config
)
# Create interactive visualizations
visualizations = await self.visualization_engine.create_visualizations(
data=enriched_data,
config=report_config.visualization_config
)
return ComprehensiveReport(
reports=reports,
visualizations=visualizations,
raw_data=enriched_data,
analytics=analytics_results,
metadata=self._generate_metadata(execution_results, report_config)
)
```
**Business Intelligence Integration**: Native integration with enterprise BI platforms including Tableau, Power BI, and Looker.
**Advanced Analytics**: Statistical analysis, trend detection, performance regression analysis, and predictive modeling.
**Compliance Reporting**: Automated generation of compliance reports for SOC 2, HIPAA, GDPR, and other regulatory frameworks.
**Real-Time Dashboards**: Live dashboards with customizable widgets, drilling capabilities, and alert integration.
## Advanced Architectural Patterns
### Microservices-Ready Architecture
MCPTesta's architecture anticipates microservices deployment scenarios:
```python
class DistributedExecutionCoordinator:
def __init__(self):
self.service_registry = ServiceRegistry()
self.distributed_scheduler = DistributedScheduler()
self.consensus_manager = ConsensusManager()
self.distributed_cache = DistributedCache()
async def coordinate_distributed_execution(self, test_suite: TestSuite) -> ExecutionResult:
# Discover available execution nodes
execution_nodes = await self.service_registry.discover_execution_nodes()
# Create distributed execution plan
distributed_plan = await self.distributed_scheduler.create_distributed_plan(
test_suite=test_suite,
available_nodes=execution_nodes
)
# Coordinate execution across nodes
execution_coordinator = DistributedExecutionContext(
plan=distributed_plan,
consensus_manager=self.consensus_manager,
cache=self.distributed_cache
)
return await execution_coordinator.execute_distributed_tests()
```
**Service Discovery**: Dynamic service discovery with health checking and load balancing.
**Distributed Coordination**: Consensus algorithms for coordinating distributed test execution.
**Fault Tolerance**: Sophisticated fault tolerance with automatic failover and recovery.
### Event Sourcing and CQRS Implementation
```python
class TestExecutionEventStore:
def __init__(self):
self.event_store = EventStore()
self.projection_manager = ProjectionManager()
self.command_handlers = CommandHandlerRegistry()
self.query_handlers = QueryHandlerRegistry()
async def handle_command(self, command: Command) -> CommandResult:
# Validate command
validation_result = await self._validate_command(command)
if not validation_result.is_valid:
raise CommandValidationError(validation_result.errors)
# Execute command and generate events
handler = self.command_handlers.get_handler(type(command))
events = await handler.handle(command)
# Store events
await self.event_store.append_events(command.aggregate_id, events)
# Update projections
await self.projection_manager.update_projections(events)
return CommandResult(success=True, events=events)
async def handle_query(self, query: Query) -> QueryResult:
handler = self.query_handlers.get_handler(type(query))
return await handler.handle(query)
```
**Event Sourcing**: Complete audit trail through event sourcing with replay capabilities.
**CQRS**: Separated read and write models optimized for their specific use cases.
**Temporal Queries**: Historical analysis and time-travel debugging capabilities.
### Advanced Security Architecture
```python
class SecurityManager:
def __init__(self):
self.auth_provider = MultiFactorAuthProvider()
self.authorization_engine = RBACAuthorizationEngine()
self.audit_logger = SecurityAuditLogger()
self.encryption_service = EncryptionService()
self.vulnerability_scanner = VulnerabilityScanner()
async def secure_execution_context(self, context: ExecutionContext) -> SecureExecutionContext:
# Authenticate user
user_identity = await self.auth_provider.authenticate(context.credentials)
# Authorize operations
permissions = await self.authorization_engine.get_permissions(user_identity)
# Create secure context
secure_context = SecureExecutionContext(
user_identity=user_identity,
permissions=permissions,
encryption_keys=await self.encryption_service.generate_session_keys(),
audit_logger=self.audit_logger
)
# Scan for vulnerabilities
vulnerability_report = await self.vulnerability_scanner.scan_context(secure_context)
secure_context.vulnerability_report = vulnerability_report
return secure_context
```
**Zero-Trust Security**: Comprehensive security model with continuous verification and minimal trust assumptions.
**End-to-End Encryption**: All data encrypted in transit and at rest with key rotation and perfect forward secrecy.
**Advanced Auditing**: Comprehensive audit trails with tamper detection and blockchain-based integrity verification.
**Vulnerability Management**: Continuous vulnerability scanning and automated remediation.
## Performance Engineering and Optimization
### Advanced Performance Monitoring
```python
class PerformanceMonitoringSystem:
def __init__(self):
self.metrics_collector = MetricsCollector()
self.performance_profiler = PerformanceProfiler()
self.bottleneck_detector = BottleneckDetector()
self.optimization_engine = AutoOptimizationEngine()
async def monitor_execution_performance(self, execution_context: ExecutionContext) -> PerformanceProfile:
# Collect comprehensive metrics
with self.metrics_collector.monitoring_session():
# CPU profiling
cpu_profile = await self.performance_profiler.profile_cpu_usage(execution_context)
# Memory profiling
memory_profile = await self.performance_profiler.profile_memory_usage(execution_context)
# Network profiling
network_profile = await self.performance_profiler.profile_network_usage(execution_context)
# I/O profiling
io_profile = await self.performance_profiler.profile_io_usage(execution_context)
# Detect bottlenecks
bottlenecks = await self.bottleneck_detector.detect_bottlenecks(
cpu_profile, memory_profile, network_profile, io_profile
)
# Generate optimization recommendations
optimizations = await self.optimization_engine.generate_optimizations(bottlenecks)
return PerformanceProfile(
cpu_usage=cpu_profile,
memory_usage=memory_profile,
network_usage=network_profile,
io_usage=io_profile,
bottlenecks=bottlenecks,
optimization_recommendations=optimizations
)
```
**Real-Time Profiling**: Continuous performance profiling with minimal overhead using statistical sampling and adaptive instrumentation.
**Machine Learning Optimization**: ML-driven performance optimization with automated parameter tuning and adaptive algorithms.
**Predictive Scaling**: Predictive resource scaling based on historical patterns and real-time workload analysis.
### Memory Management and Resource Optimization
```python
class AdvancedResourceManager:
def __init__(self):
self.memory_pool = ManagedMemoryPool()
self.connection_pool = AdaptiveConnectionPool()
self.cpu_scheduler = CPUScheduler()
self.gc_optimizer = GarbageCollectionOptimizer()
async def optimize_resource_usage(self, execution_plan: ExecutionPlan) -> ResourceOptimizationPlan:
# Analyze resource requirements
resource_analysis = await self._analyze_resource_requirements(execution_plan)
# Optimize memory allocation
memory_plan = await self.memory_pool.create_allocation_plan(resource_analysis.memory_requirements)
# Optimize connection usage
connection_plan = await self.connection_pool.create_connection_plan(resource_analysis.connection_requirements)
# Optimize CPU scheduling
cpu_plan = await self.cpu_scheduler.create_scheduling_plan(resource_analysis.cpu_requirements)
# Optimize garbage collection
gc_plan = await self.gc_optimizer.create_gc_plan(resource_analysis.gc_requirements)
return ResourceOptimizationPlan(
memory_allocation=memory_plan,
connection_management=connection_plan,
cpu_scheduling=cpu_plan,
garbage_collection=gc_plan
)
```
**Advanced Memory Management**: Sophisticated memory management with pool allocation, garbage collection optimization, and memory leak detection.
**Dynamic Resource Allocation**: Intelligent resource allocation based on workload characteristics and performance requirements.
**Resource Prediction**: Predictive resource allocation using machine learning models trained on historical execution data.
## Enterprise Integration Capabilities
### CI/CD Platform Integration
```python
class CICDIntegrationManager:
def __init__(self):
self.pipeline_integrators = {
'jenkins': JenkinsIntegrator(),
'github_actions': GitHubActionsIntegrator(),
'azure_devops': AzureDevOpsIntegrator(),
'gitlab_ci': GitLabCIIntegrator(),
'buildkite': BuildkiteIntegrator()
}
self.artifact_managers = ArtifactManagerRegistry()
self.notification_service = CICDNotificationService()
async def integrate_with_pipeline(self, pipeline_config: PipelineConfig) -> PipelineIntegration:
integrator = self.pipeline_integrators[pipeline_config.platform]
# Set up pipeline integration
integration = await integrator.setup_integration(pipeline_config)
# Configure artifact management
artifact_manager = self.artifact_managers.get_manager(pipeline_config.artifact_config)
await integration.configure_artifact_management(artifact_manager)
# Set up notifications
await self.notification_service.configure_pipeline_notifications(
integration, pipeline_config.notification_config
)
return integration
```
**Universal CI/CD Support**: Native integration with all major CI/CD platforms with platform-specific optimizations.
**Artifact Management**: Sophisticated artifact management with versioning, dependency tracking, and automated cleanup.
**Pipeline Orchestration**: Advanced pipeline orchestration with conditional execution, parallel stages, and sophisticated workflow management.
### Observability Platform Integration
```python
class ObservabilityIntegrationManager:
def __init__(self):
self.telemetry_exporters = {
'opentelemetry': OpenTelemetryExporter(),
'jaeger': JaegerExporter(),
'zipkin': ZipkinExporter(),
'datadog': DatadogExporter(),
'new_relic': NewRelicExporter(),
'prometheus': PrometheusExporter()
}
self.log_shippers = LogShipperRegistry()
self.metrics_aggregators = MetricsAggregatorRegistry()
async def setup_observability(self, observability_config: ObservabilityConfig) -> ObservabilityContext:
# Configure distributed tracing
trace_exporter = self.telemetry_exporters[observability_config.tracing.provider]
tracing_context = await trace_exporter.setup_tracing(observability_config.tracing)
# Configure metrics collection
metrics_aggregator = self.metrics_aggregators.get_aggregator(observability_config.metrics.provider)
metrics_context = await metrics_aggregator.setup_metrics(observability_config.metrics)
# Configure log shipping
log_shipper = self.log_shippers.get_shipper(observability_config.logging.provider)
logging_context = await log_shipper.setup_logging(observability_config.logging)
return ObservabilityContext(
tracing=tracing_context,
metrics=metrics_context,
logging=logging_context
)
```
**Comprehensive Observability**: Full observability stack with distributed tracing, metrics collection, and centralized logging.
**Vendor Agnostic**: Support for all major observability platforms with automatic format conversion and protocol adaptation.
**Intelligent Sampling**: Adaptive sampling strategies that balance observability coverage with performance impact.
## Future-Proofing and Extensibility
### Plugin Architecture for Extensibility
```python
class PluginManager:
def __init__(self):
self.plugin_registry = PluginRegistry()
self.extension_points = ExtensionPointRegistry()
self.dependency_resolver = PluginDependencyResolver()
self.security_validator = PluginSecurityValidator()
async def load_plugin(self, plugin_spec: PluginSpecification) -> LoadedPlugin:
# Validate plugin security
security_assessment = await self.security_validator.assess_plugin(plugin_spec)
if not security_assessment.is_safe:
raise UnsafePluginError(security_assessment.issues)
# Resolve dependencies
dependencies = await self.dependency_resolver.resolve_dependencies(plugin_spec)
# Load plugin with isolation
plugin = await self._load_plugin_with_isolation(plugin_spec, dependencies)
# Register extension points
extension_points = await plugin.get_extension_points()
await self.extension_points.register_extensions(plugin, extension_points)
return LoadedPlugin(plugin=plugin, dependencies=dependencies)
```
**Secure Plugin System**: Comprehensive plugin security with sandboxing, permission models, and vulnerability scanning.
**Dependency Management**: Sophisticated plugin dependency resolution with version compatibility and conflict detection.
**Hot-Pluggable Extensions**: Dynamic plugin loading and unloading without system restart.
### Protocol Evolution Support
```python
class ProtocolEvolutionManager:
def __init__(self):
self.version_registry = ProtocolVersionRegistry()
self.compatibility_engine = CompatibilityEngine()
self.migration_engine = MigrationEngine()
self.feature_flag_manager = FeatureFlagManager()
async def handle_protocol_evolution(self, server_info: ServerInfo) -> ProtocolAdapter:
# Detect protocol version
detected_version = await self.version_registry.detect_protocol_version(server_info)
# Check compatibility
compatibility = await self.compatibility_engine.assess_compatibility(
detected_version, MCPTesta.supported_versions
)
if compatibility.requires_adaptation:
# Create protocol adapter
adapter = await self._create_protocol_adapter(detected_version, compatibility)
return adapter
# Handle migration scenarios
if compatibility.requires_migration:
migration_plan = await self.migration_engine.create_migration_plan(
from_version=detected_version,
to_version=MCPTesta.preferred_version
)
return await self.migration_engine.execute_migration(migration_plan)
return DirectProtocolAdapter(detected_version)
```
**Version Compatibility**: Automatic handling of multiple MCP protocol versions with seamless adaptation.
**Feature Detection**: Dynamic feature detection and graceful degradation for unsupported features.
**Migration Assistance**: Automated migration tools for upgrading servers and configurations to newer protocol versions.
## Conclusion: Enterprise Architecture Excellence
MCPTesta's architecture represents a synthesis of modern software engineering principles, enterprise requirements, and practical testing needs. The sophisticated design patterns, advanced performance optimizations, and comprehensive enterprise integrations position MCPTesta as a production-ready testing framework capable of handling the most demanding requirements.
The architecture's key strengths include:
**Sophisticated Modularity**: Clean separation of concerns with well-defined interfaces enables independent evolution of components while maintaining system coherence.
**Enterprise-Grade Reliability**: Advanced fault tolerance, circuit breakers, and recovery mechanisms ensure robust operation in production environments.
**Performance Excellence**: Sophisticated optimization algorithms, resource management, and performance monitoring deliver exceptional performance at scale.
**Security by Design**: Comprehensive security architecture with zero-trust principles, end-to-end encryption, and advanced auditing capabilities.
**Future-Proof Extensibility**: Plugin architecture and protocol evolution support ensure long-term adaptability as requirements evolve.
**Operational Excellence**: Deep integration with enterprise toolchains, observability platforms, and operational workflows.
This architectural foundation enables MCPTesta to serve as the definitive testing framework for the FastMCP ecosystem, supporting everything from individual developer workflows to enterprise-scale compliance validation and performance testing. The sophisticated engineering ensures that MCPTesta can evolve with the MCP protocol and testing requirements while maintaining backward compatibility and operational stability.
Understanding this architecture empowers users to leverage MCPTesta's full capabilities and provides a blueprint for extending the framework to meet specific organizational requirements. The design patterns and engineering principles demonstrated in MCPTesta reflect industry best practices and can serve as a reference for building sophisticated, enterprise-grade testing infrastructure.

View File

@ -0,0 +1,344 @@
---
title: Understanding MCP Protocol Testing
description: Fundamental concepts behind testing MCP servers and comprehensive protocol validation
---
This explanation explores the fundamental concepts behind testing MCP (Model Context Protocol) servers and how MCPTesta implements comprehensive protocol validation.
## What is the MCP Protocol?
The Model Context Protocol (MCP) is a standardized way for AI models to interact with external tools, resources, and data sources. Unlike simple API calls, MCP provides a rich, bidirectional communication protocol that enables:
- **Tool Discovery and Execution**: Dynamic discovery and invocation of available tools
- **Resource Access**: Structured access to files, databases, and external services
- **Prompt Management**: Template-based prompt generation and customization
- **Real-time Communication**: Bidirectional messaging with notifications and progress updates
FastMCP is a Python implementation that makes building MCP servers straightforward and efficient.
## Why Testing MCP Servers is Complex
MCP protocol testing presents unique challenges that distinguish it from traditional API testing:
### Protocol Complexity
**Bidirectional Communication**: Unlike REST APIs, MCP involves two-way communication where servers can send unsolicited notifications to clients.
**State Management**: MCP connections maintain state across multiple interactions, requiring careful validation of connection lifecycle management.
**Dynamic Capabilities**: Servers expose capabilities dynamically, requiring discovery-based testing rather than static endpoint validation.
### Temporal Aspects
**Asynchronous Operations**: Many MCP operations are inherently asynchronous, with progress updates and eventual completion.
**Long-Running Operations**: Some tools may execute for extended periods, requiring timeout management and cancellation support.
**Notification Timing**: Server-initiated notifications can arrive at unpredictable times, requiring event-driven testing approaches.
### Protocol Features
**Multiple Transports**: MCP supports stdio, Server-Sent Events (SSE), and WebSocket transports, each with different characteristics.
**Authentication Variations**: Different servers may implement bearer tokens, OAuth, or custom authentication schemes.
**Progress Reporting**: Advanced servers provide real-time progress updates for long-running operations.
**Request Cancellation**: Sophisticated implementations support graceful operation cancellation.
## MCPTesta's Testing Philosophy
MCPTesta approaches MCP testing with several key principles:
### Comprehensive Protocol Coverage
Rather than testing just the "happy path," MCPTesta validates the entire MCP specification:
**Core Protocol Elements**:
- Connection establishment and teardown
- Capability discovery and negotiation
- Tool discovery, parameter validation, and execution
- Resource access and content validation
- Prompt generation and template processing
**Advanced Protocol Features**:
- Notification subscription and handling
- Progress monitoring and reporting
- Request cancellation and cleanup
- Sampling mechanisms and rate limiting
### Real-World Scenario Testing
MCPTesta doesn't just test individual protocol messages; it validates complete workflows:
**Workflow Validation**: Testing sequences of operations that mirror real application usage patterns.
**Error Scenario Coverage**: Validating how servers handle malformed requests, missing parameters, and resource constraints.
**Performance Characteristics**: Understanding how servers behave under load and with concurrent operations.
### Protocol Feature Detection
MCPTesta automatically discovers what features each server supports and adjusts its testing accordingly:
```python
# MCPTesta detects server capabilities
capabilities = await client.discover_capabilities()
if capabilities.supports_notifications:
await test_notification_features(client)
if capabilities.supports_progress:
await test_progress_reporting(client)
if capabilities.supports_cancellation:
await test_cancellation_features(client)
```
This approach ensures that tests are relevant to each server's actual capabilities while still validating protocol compliance.
## Testing Transport Layers
Different MCP transports have distinct characteristics that affect testing:
### stdio Transport
**Characteristics**:
- Process-based communication using stdin/stdout
- Synchronous message exchange
- Process lifecycle management
- No built-in authentication
**Testing Considerations**:
- Server startup and shutdown validation
- Process health monitoring
- Input/output stream management
- Signal handling and graceful termination
### SSE (Server-Sent Events) Transport
**Characteristics**:
- HTTP-based unidirectional streaming from server to client
- Built-in reconnection handling
- HTTP authentication support
- Real-time server-to-client communication
**Testing Considerations**:
- HTTP connection establishment
- Stream parsing and message framing
- Connection resilience and reconnection
- Authentication header validation
### WebSocket Transport
**Characteristics**:
- Full-duplex communication over persistent connections
- Low-latency bidirectional messaging
- Built-in ping/pong for connection health
- Subprotocol negotiation
**Testing Considerations**:
- WebSocket handshake validation
- Bidirectional message flow
- Connection health monitoring
- Subprotocol compliance
## Advanced Protocol Features
### Notification System
MCP's notification system enables servers to proactively inform clients about state changes:
**Resource List Changes**: When available resources are modified, added, or removed.
**Tool List Changes**: When server capabilities change during runtime.
**Prompt List Changes**: When available prompts are updated.
**Custom Notifications**: Server-specific events and state changes.
MCPTesta validates notification behavior by:
1. Subscribing to notification types
2. Triggering actions that should generate notifications
3. Verifying that notifications arrive with correct content and timing
4. Testing notification unsubscription
### Progress Reporting
For long-running operations, MCP supports real-time progress updates:
**Progress Tokens**: Unique identifiers for tracking operation progress.
**Progress Updates**: Periodic status reports with completion percentages and status messages.
**Progress Completion**: Final status indicating success or failure.
MCPTesta tests progress reporting by:
1. Initiating operations that support progress tracking
2. Monitoring progress update frequency and content
3. Validating progress token consistency
4. Verifying completion notifications
### Request Cancellation
MCP allows clients to cancel long-running operations gracefully:
**Cancellation Requests**: Client-initiated operation termination.
**Cleanup Handling**: Server-side resource cleanup after cancellation.
**Graceful Termination**: Ensuring operations stop in a clean state.
MCPTesta validates cancellation by:
1. Starting long-running operations
2. Sending cancellation requests at various points
3. Verifying that operations terminate promptly
4. Checking that server resources are properly cleaned up
### Sampling Mechanisms
Advanced MCP implementations may support request sampling for load management:
**Sampling Rates**: Configurable percentages of requests to process.
**Sampling Strategies**: Random, round-robin, or weighted sampling approaches.
**Load Shedding**: Graceful handling of excess load through sampling.
MCPTesta tests sampling by:
1. Configuring various sampling rates
2. Sending multiple requests and measuring actual sampling behavior
3. Validating that sampling decisions are consistent with configuration
4. Testing edge cases like 0% and 100% sampling rates
## Error Handling and Edge Cases
MCPTesta places significant emphasis on testing error conditions and edge cases:
### Protocol-Level Errors
**Malformed Messages**: Testing with invalid JSON, missing fields, and incorrect data types.
**Invalid Sequences**: Sending messages in incorrect order or without proper initialization.
**Resource Constraints**: Testing behavior when servers reach memory, connection, or processing limits.
### Application-Level Errors
**Tool Errors**: Validating how servers handle and report tool execution failures.
**Resource Access Errors**: Testing responses to missing files, network failures, and permission issues.
**Authentication Errors**: Verifying proper handling of expired tokens, invalid credentials, and authorization failures.
### Recovery Scenarios
**Connection Recovery**: Testing behavior after network interruptions or server restarts.
**State Recovery**: Validating that servers can rebuild state after connection loss.
**Resource Recovery**: Ensuring that temporary resource failures don't cause permanent issues.
## Performance and Scalability Testing
MCPTesta understands that MCP servers must perform well under real-world conditions:
### Concurrent Operation Testing
**Multiple Clients**: Simulating multiple simultaneous client connections.
**Parallel Requests**: Testing server behavior with concurrent tool calls and resource access.
**Resource Contention**: Validating fair resource allocation among competing requests.
### Load Characteristics
**Sustained Load**: Testing server performance under continuous high load.
**Burst Load**: Validating handling of sudden traffic spikes.
**Memory Pressure**: Testing behavior as memory usage approaches system limits.
### Performance Metrics
**Response Times**: Measuring latency for different operation types.
**Throughput**: Determining maximum sustainable request rates.
**Resource Usage**: Monitoring CPU, memory, and network utilization.
## Integration Testing Concepts
MCPTesta recognizes that MCP servers don't exist in isolation:
### External Dependencies
**Database Connections**: Testing with various database states and connection issues.
**API Dependencies**: Validating behavior when external APIs are slow or unavailable.
**File System Access**: Testing with different file system permissions and storage conditions.
### Environment Variations
**Configuration Changes**: Testing with different server configurations and environment variables.
**Network Conditions**: Validating behavior under various network latency and reliability conditions.
**Resource Availability**: Testing with limited CPU, memory, or storage resources.
## Testing Strategy Selection
MCPTesta supports different testing strategies for different goals:
### Development Testing
**Rapid Feedback**: Quick tests for immediate feedback during development.
**Feature Validation**: Focused tests for new functionality.
**Regression Detection**: Automated tests to catch breaking changes.
### Integration Testing
**Workflow Validation**: Complete user journey testing.
**System Integration**: Testing interactions with external systems.
**Performance Validation**: Ensuring acceptable performance characteristics.
### Production Validation
**Health Monitoring**: Continuous validation of live systems.
**Deployment Validation**: Pre-production testing of new deployments.
**Monitoring Integration**: Feeding test results into monitoring and alerting systems.
## The Future of MCP Testing
As the MCP protocol evolves, testing approaches must adapt:
### Protocol Evolution
**New Features**: Testing frameworks must accommodate new MCP capabilities.
**Backward Compatibility**: Ensuring that servers maintain compatibility with older clients.
**Protocol Extensions**: Supporting custom protocol extensions and vendor-specific features.
### Testing Innovation
**AI-Assisted Testing**: Using AI to generate more comprehensive test scenarios.
**Property-Based Testing**: Generating tests based on protocol properties rather than specific scenarios.
**Chaos Engineering**: Introducing controlled failures to test system resilience.
## Conclusion
Testing MCP servers requires understanding both the protocol's technical specifications and its real-world usage patterns. MCPTesta provides comprehensive testing capabilities that go beyond simple request-response validation to cover the full spectrum of MCP protocol features.
By understanding these concepts, you can design more effective test strategies, identify potential issues earlier in development, and build more robust MCP servers that perform well in production environments.
The complexity of MCP protocol testing is not a burden—it's an opportunity to build better, more reliable systems that can handle the sophisticated interactions that modern AI applications require.

View File

@ -0,0 +1,497 @@
---
title: Testing Strategies
description: Methodologies and best practices for comprehensive FastMCP server testing
---
This explanation explores different approaches to FastMCP server testing, when to use each strategy, and how MCPTesta supports various testing methodologies to ensure comprehensive validation.
## The Testing Pyramid for MCP Servers
Traditional testing pyramids don't directly apply to MCP server testing due to the protocol's unique characteristics. MCPTesta adopts a modified approach:
### Protocol Compliance Testing (Foundation)
At the base of our testing pyramid is protocol compliance—ensuring your server correctly implements the MCP specification:
**Message Format Validation**: Verifying that all messages conform to MCP protocol structure.
**Transport Compliance**: Ensuring correct behavior across stdio, SSE, and WebSocket transports.
**Error Handling**: Validating proper error responses for invalid requests.
**Lifecycle Management**: Testing connection establishment, maintenance, and termination.
This foundation ensures your server can communicate with any MCP-compliant client.
### Functional Testing (Core)
The middle layer focuses on business logic and feature validation:
**Tool Functionality**: Verifying that each tool performs its intended function with correct inputs and outputs.
**Resource Access**: Testing that resources return expected content and handle access control properly.
**Prompt Generation**: Validating that prompts generate appropriate content for given arguments.
**State Management**: Ensuring that server state remains consistent across operations.
### Integration Testing (Expansion)
The upper layer tests real-world scenarios and system interactions:
**Workflow Testing**: Complete user journeys that involve multiple tool calls and resource access.
**External Dependencies**: Testing interactions with databases, APIs, and file systems.
**Performance Under Load**: Validating behavior with realistic usage patterns.
**Environment Variations**: Testing across different deployment environments and configurations.
## Testing Methodologies
### Black Box Testing
Black box testing treats the MCP server as an opaque system, validating only external behavior:
**Advantages**:
- Tests from the user's perspective
- Validates the complete system behavior
- Doesn't require knowledge of internal implementation
- Catches integration issues
**MCPTesta Support**:
```yaml
test_suites:
- name: "Black Box Validation"
tests:
- name: "user_workflow"
test_type: "tool_call"
target: "complex_operation"
parameters:
user_input: "realistic_data"
expected:
output_format: "expected_structure"
performance: { response_time: "<5.0" }
```
**When to Use**:
- Acceptance testing
- Regression testing
- User story validation
- Production health checks
### White Box Testing
White box testing leverages knowledge of the server's internal structure:
**Advantages**:
- Can test specific code paths
- Validates internal state consistency
- Enables targeted edge case testing
- Helps optimize performance
**MCPTesta Support**:
```yaml
test_suites:
- name: "Internal State Testing"
tests:
- name: "cache_invalidation"
test_type: "tool_call"
target: "cache_dependent_tool"
setup:
populate_cache: true
validation:
internal_state: "cache_populated"
- name: "trigger_cache_clear"
test_type: "tool_call"
target: "cache_clearing_tool"
depends_on: ["cache_invalidation"]
validation:
internal_state: "cache_empty"
```
**When to Use**:
- Unit testing individual tools
- Performance optimization
- Bug reproduction
- Security testing
### Gray Box Testing
Gray box testing combines black box and white box approaches:
**Advantages**:
- Balances external perspective with internal knowledge
- Enables more targeted testing
- Provides better error diagnosis
- Supports both functional and structural validation
**MCPTesta Support**:
```yaml
test_suites:
- name: "Gray Box Analysis"
tests:
- name: "error_path_testing"
test_type: "tool_call"
target: "error_prone_tool"
parameters:
trigger_condition: "known_failure_mode"
expected_error:
type: "SpecificException"
internal_state: "error_logged"
enable_performance_profiling: true
```
**When to Use**:
- API testing
- Security validation
- Performance analysis
- Debugging complex issues
## Property-Based Testing
Property-based testing generates test inputs automatically based on specified properties:
### Defining Properties
Instead of writing specific test cases, you define properties that should always hold:
```yaml
test_suites:
- name: "Property Based Testing"
tests:
- name: "calculator_properties"
test_type: "property_test"
target: "calculator"
properties:
- name: "addition_commutative"
rule: "add(a,b) == add(b,a)"
generators:
a: { type: "integer", range: [-1000, 1000] }
b: { type: "integer", range: [-1000, 1000] }
- name: "division_by_zero"
rule: "divide(a, 0) raises ValueError"
generators:
a: { type: "integer", range: [-1000, 1000] }
```
### Advantages of Property-Based Testing
**Comprehensive Coverage**: Automatically tests with a wide range of inputs.
**Edge Case Discovery**: Often finds edge cases that manual testing misses.
**Regression Prevention**: Continues testing with new random inputs on each run.
**Documentation**: Properties serve as executable documentation of system behavior.
## Chaos Engineering for MCP Servers
Chaos engineering introduces controlled failures to test system resilience:
### Infrastructure Chaos
**Network Failures**: Testing behavior when network connections are interrupted.
**Resource Exhaustion**: Validating graceful degradation when memory or CPU is constrained.
**Dependency Failures**: Testing responses to external service outages.
```yaml
test_suites:
- name: "Chaos Testing"
chaos_config:
enabled: true
failure_rate: 0.1 # 10% of operations fail
failure_types: ["network", "memory", "timeout"]
tests:
- name: "resilience_test"
test_type: "tool_call"
target: "critical_tool"
chaos_scenarios:
- type: "network_partition"
duration: 5
recovery_expected: true
- type: "memory_pressure"
severity: "high"
graceful_degradation: true
```
### Application-Level Chaos
**Invalid Inputs**: Sending malformed or unexpected data to test error handling.
**Timing Attacks**: Introducing delays at various points to test timeout handling.
**State Corruption**: Testing recovery from inconsistent internal state.
## Performance Testing Strategies
### Load Testing
Validating normal expected load handling:
```yaml
test_suites:
- name: "Load Testing"
performance_config:
concurrent_users: 50
test_duration: 300 # 5 minutes
ramp_up_time: 60 # 1 minute
tests:
- name: "sustained_load"
test_type: "tool_call"
target: "primary_tool"
load_profile: "constant"
success_criteria:
response_time_p95: "<2.0"
error_rate: "<0.01"
```
### Stress Testing
Finding breaking points and system limits:
```yaml
test_suites:
- name: "Stress Testing"
performance_config:
max_concurrent_users: 500
escalation_strategy: "gradual"
stop_on_failure: false
tests:
- name: "breaking_point"
test_type: "tool_call"
target: "resource_intensive_tool"
load_profile: "escalating"
monitoring:
- "memory_usage"
- "cpu_utilization"
- "response_times"
```
### Spike Testing
Testing response to sudden load increases:
```yaml
test_suites:
- name: "Spike Testing"
tests:
- name: "traffic_spike"
test_type: "tool_call"
target: "scalable_tool"
load_profile:
- phase: "baseline"
users: 10
duration: 60
- phase: "spike"
users: 200
duration: 30
- phase: "recovery"
users: 10
duration: 60
```
## Security Testing Approaches
### Authentication Testing
**Token Validation**: Testing with valid, invalid, expired, and malformed tokens.
**Authorization Checks**: Ensuring users can only access authorized resources.
**Session Management**: Testing session lifecycle and security.
```yaml
test_suites:
- name: "Security Testing"
security_config:
enable_auth_testing: true
token_scenarios: ["valid", "expired", "invalid", "malformed"]
tests:
- name: "auth_boundary_testing"
test_type: "tool_call"
target: "protected_tool"
auth_scenarios:
- token_type: "expired"
expected_error: "AuthenticationError"
- token_type: "insufficient_scope"
expected_error: "AuthorizationError"
```
### Input Validation Testing
**Injection Attacks**: Testing SQL injection, command injection, and script injection.
**Buffer Overflows**: Sending oversized inputs to test bounds checking.
**Format String Attacks**: Testing with format string vulnerabilities.
### Data Security Testing
**Sensitive Data Handling**: Ensuring credentials and personal data are properly protected.
**Data Encryption**: Testing encryption of data in transit and at rest.
**Audit Logging**: Validating that security events are properly logged.
## Continuous Testing Strategies
### Shift-Left Testing
Moving testing earlier in the development cycle:
**Developer Testing**: Running MCPTesta during local development.
**Pre-Commit Hooks**: Automated testing before code commits.
**Feature Branch Testing**: Validating changes before merging.
```bash
# Pre-commit hook example
#!/bin/bash
echo "Running MCPTesta pre-commit validation..."
mcptesta yaml .mcptesta/pre-commit-tests.yaml --parallel 4
if [ $? -ne 0 ]; then
echo "Tests failed - commit rejected"
exit 1
fi
```
### Continuous Integration Testing
**Pull Request Validation**: Comprehensive testing for all proposed changes.
**Multi-Environment Testing**: Testing across development, staging, and production-like environments.
**Parallel Test Execution**: Using MCPTesta's parallel capabilities for fast feedback.
### Production Testing
**Canary Testing**: Gradual rollout with continuous validation.
**Health Monitoring**: Ongoing validation of production systems.
**Synthetic Testing**: Automated testing of production systems with synthetic workloads.
```yaml
# Production monitoring configuration
config:
monitoring_mode: true
alert_on_failure: true
failure_threshold: 0.01 # 1% failure rate
test_suites:
- name: "Production Health"
schedule: "*/5 * * * *" # Every 5 minutes
tests:
- name: "critical_path"
test_type: "tool_call"
target: "health_check"
alert_level: "critical"
```
## Test Environment Strategies
### Environment Parity
Ensuring test environments accurately reflect production:
**Configuration Management**: Using the same configuration mechanisms across environments.
**Data Similarity**: Testing with production-like data volumes and characteristics.
**Infrastructure Matching**: Using similar hardware and network configurations.
### Test Data Management
**Synthetic Data Generation**: Creating realistic test data that doesn't contain sensitive information.
**Data Masking**: Protecting sensitive data in test environments.
**State Management**: Ensuring tests start with known, clean state.
```yaml
test_suites:
- name: "Data-Driven Testing"
data_config:
source: "synthetic_generator"
volume: "production_scale"
sensitive_data: "masked"
setup:
- action: "reset_database"
- action: "seed_test_data"
source: "${TEST_DATA_SOURCE}"
teardown:
- action: "cleanup_test_data"
- action: "reset_state"
```
## Risk-Based Testing
### Priority-Based Test Selection
Not all tests are equally important. MCPTesta supports risk-based testing:
**Critical Path Testing**: Focusing on functionality that's essential for business operations.
**High-Risk Areas**: Prioritizing testing of complex or frequently changing code.
**User Impact Assessment**: Weighing testing effort against potential user impact.
```yaml
test_suites:
- name: "Critical Path"
priority: "critical"
execution_policy: "always_run"
tests:
- name: "core_business_logic"
criticality: "high"
business_impact: "revenue_affecting"
- name: "Edge Cases"
priority: "low"
execution_policy: "time_permitting"
tests:
- name: "rare_scenario"
criticality: "low"
business_impact: "minimal"
```
### Test Budget Management
**Time Constraints**: Optimizing test selection when time is limited.
**Resource Constraints**: Adapting testing strategy based on available compute resources.
**Cost-Benefit Analysis**: Balancing test coverage against execution cost.
## Conclusion
Effective MCPTesta usage requires understanding different testing strategies and applying them appropriately:
**Start with Protocol Compliance**: Ensure your server correctly implements MCP before testing business logic.
**Layer Your Testing**: Use the modified testing pyramid to build comprehensive coverage.
**Choose Appropriate Methodologies**: Select black box, white box, or gray box testing based on your goals.
**Embrace Automation**: Use property-based testing and chaos engineering to discover issues that manual testing might miss.
**Consider the Context**: Adapt your testing strategy based on risk, resources, and requirements.
**Iterate and Improve**: Continuously refine your testing approach based on what you learn.
MCPTesta provides the tools to implement all these strategies effectively. The key is understanding when and how to apply each approach to build confidence in your FastMCP server's reliability, performance, and security.
Remember that testing is not just about finding bugs—it's about understanding your system's behavior under various conditions and ensuring it meets the needs of its users. A well-designed testing strategy using MCPTesta helps you build better FastMCP servers and maintain them effectively over time.

View File

@ -0,0 +1,720 @@
---
title: CI/CD Integration
description: Integrate MCPTesta into your CI/CD pipelines with Git workflows, Jenkins, and more
---
This guide shows you how to integrate MCPTesta into continuous integration and deployment pipelines, ensuring your FastMCP servers are validated automatically with every change.
## Problem scenarios
Use this guide when you need to:
- Automatically test FastMCP servers in CI/CD pipelines
- Generate test reports for build systems
- Gate deployments based on test results
- Set up testing across multiple environments
- Integrate with existing DevOps workflows
## Prerequisites
- CI/CD system (Git workflows, GitLab CI, Jenkins, etc.)
- FastMCP server with source code in version control
- Understanding of your CI/CD platform's configuration format
- Access to configure build pipelines
## Git workflow integration
### Basic workflow setup
Create `.gitea/workflows/mcptesta.yml`:
```yaml
name: MCPTesta FastMCP Server Tests
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.11'
- name: Install uv
uses: astral-sh/setup-uv@v1
- name: Install MCPTesta
run: |
git clone https://git.supported.systems/mcp/mcptesta.git
cd mcptesta
uv sync
- name: Install FastMCP server dependencies
run: |
uv sync # Install your server's dependencies
- name: Run MCPTesta tests
run: |
cd mcptesta
uv run mcptesta test \
--server "python ../my_fastmcp_server.py" \
--format junit \
--output ./test-results.xml \
--parallel 4
- name: Upload test results
uses: actions/upload-artifact@v3
if: always()
with:
name: test-results
path: mcptesta/test-results.xml
- name: Publish test results
uses: dorny/test-reporter@v1
if: always()
with:
name: MCPTesta Results
path: mcptesta/test-results.xml
reporter: java-junit
```
### Advanced workflow with multiple environments
```yaml
name: Multi-Environment MCPTesta
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
environment: [development, staging, production]
python-version: ['3.11', '3.12']
steps:
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
git clone https://git.supported.systems/mcp/mcptesta.git
cd mcptesta && uv sync
uv sync # Server dependencies
- name: Run environment-specific tests
env:
ENVIRONMENT: ${{ matrix.environment }}
AUTH_TOKEN: ${{ secrets.AUTH_TOKEN }}
run: |
cd mcptesta
uv run mcptesta yaml ../tests/${{ matrix.environment }}_tests.yaml \
--format junit \
--output ./results-${{ matrix.environment }}-py${{ matrix.python-version }}.xml
- name: Upload results
uses: actions/upload-artifact@v3
if: always()
with:
name: test-results-${{ matrix.environment }}-py${{ matrix.python-version }}
path: mcptesta/results-*.xml
```
## GitLab CI integration
### Basic GitLab CI configuration
Create `.gitlab-ci.yml`:
```yaml
stages:
- test
- report
variables:
PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache/pip"
cache:
paths:
- .cache/pip
- .venv/
before_script:
- python -m venv .venv
- source .venv/bin/activate
- pip install uv
mcptesta_tests:
stage: test
image: python:3.11
script:
- git clone https://git.supported.systems/mcp/mcptesta.git
- cd mcptesta && uv sync
- cd ..
- uv sync # Install server dependencies
- cd mcptesta
- uv run mcptesta yaml ../ci_tests.yaml
--format junit
--output ./junit-report.xml
--parallel 4
artifacts:
when: always
reports:
junit: mcptesta/junit-report.xml
paths:
- mcptesta/junit-report.xml
expire_in: 1 week
performance_tests:
stage: test
image: python:3.11
script:
- cd mcptesta
- uv run mcptesta yaml ../performance_tests.yaml
--format html
--output ./performance-report
--performance-profile
--memory-profile
artifacts:
paths:
- mcptesta/performance-report/
expire_in: 1 week
only:
- main
- develop
```
### Multi-stage pipeline
```yaml
stages:
- build
- unit-test
- integration-test
- performance-test
- deploy
build_server:
stage: build
script:
- uv sync
- uv run python -m py_compile my_fastmcp_server.py
artifacts:
paths:
- .venv/
expire_in: 1 hour
unit_tests:
stage: unit-test
dependencies:
- build_server
script:
- source .venv/bin/activate
- cd mcptesta
- uv run mcptesta test
--server "python ../my_fastmcp_server.py"
--format junit
--output ./unit-results.xml
integration_tests:
stage: integration-test
dependencies:
- unit_tests
script:
- cd mcptesta
- uv run mcptesta yaml ../integration_tests.yaml
--format junit
--output ./integration-results.xml
performance_tests:
stage: performance-test
dependencies:
- integration_tests
script:
- cd mcptesta
- uv run mcptesta yaml ../performance_tests.yaml
--stress-test
--performance-profile
only:
- main
```
## Jenkins integration
### Jenkinsfile pipeline
Create `Jenkinsfile`:
```groovy
pipeline {
agent any
environment {
PYTHON_VERSION = '3.11'
MCPTESTA_OUTPUT = 'test-results'
}
stages {
stage('Setup') {
steps {
sh '''
python${PYTHON_VERSION} -m venv venv
. venv/bin/activate
pip install uv
git clone https://git.supported.systems/mcp/mcptesta.git
cd mcptesta && uv sync
cd .. && uv sync
'''
}
}
stage('Unit Tests') {
steps {
sh '''
. venv/bin/activate
cd mcptesta
uv run mcptesta test \
--server "python ../my_fastmcp_server.py" \
--format junit \
--output ./${MCPTESTA_OUTPUT}/unit-tests.xml \
--parallel 4
'''
}
post {
always {
junit 'mcptesta/test-results/unit-tests.xml'
}
}
}
stage('Integration Tests') {
when {
anyOf {
branch 'main'
branch 'develop'
}
}
steps {
sh '''
. venv/bin/activate
cd mcptesta
uv run mcptesta yaml ../integration_tests.yaml \
--format junit \
--output ./${MCPTESTA_OUTPUT}/integration-tests.xml
'''
}
post {
always {
junit 'mcptesta/test-results/integration-tests.xml'
}
}
}
stage('Performance Tests') {
when {
branch 'main'
}
steps {
sh '''
. venv/bin/activate
cd mcptesta
uv run mcptesta yaml ../performance_tests.yaml \
--format html \
--output ./${MCPTESTA_OUTPUT}/performance \
--performance-profile \
--memory-profile
'''
}
post {
always {
publishHTML([
allowMissing: false,
alwaysLinkToLastBuild: true,
keepAll: true,
reportDir: 'mcptesta/test-results/performance',
reportFiles: 'index.html',
reportName: 'MCPTesta Performance Report'
])
}
}
}
}
post {
always {
archiveArtifacts artifacts: 'mcptesta/test-results/**/*', fingerprint: true
}
failure {
emailext (
subject: "MCPTesta Tests Failed: ${env.JOB_NAME} - ${env.BUILD_NUMBER}",
body: "Build failed. Check console output at ${env.BUILD_URL}",
to: "${env.CHANGE_AUTHOR_EMAIL}"
)
}
}
}
```
## Docker-based CI testing
### Multi-stage Docker testing
Create `Dockerfile.test`:
```dockerfile
FROM python:3.11-slim as base
WORKDIR /app
# Install uv
RUN pip install uv
# Copy server code
COPY . .
RUN uv sync
# Test stage
FROM base as test
# Clone and install MCPTesta
RUN git clone https://git.supported.systems/mcp/mcptesta.git
WORKDIR /app/mcptesta
RUN uv sync
# Run tests
RUN uv run mcptesta test \
--server "python ../my_fastmcp_server.py" \
--format junit \
--output ./test-results.xml
# Production stage
FROM base as production
EXPOSE 8000
CMD ["uv", "run", "python", "my_fastmcp_server.py"]
```
### Docker Compose for testing
Create `docker-compose.test.yml`:
```yaml
version: '3.8'
services:
fastmcp-server:
build:
context: .
target: base
command: python my_fastmcp_server.py
environment:
- ENVIRONMENT=test
healthcheck:
test: ["CMD", "python", "-c", "import requests; requests.get('http://localhost:8000/health')"]
interval: 10s
timeout: 5s
retries: 5
mcptesta:
build:
context: .
dockerfile: Dockerfile.mcptesta
depends_on:
fastmcp-server:
condition: service_healthy
volumes:
- ./test-results:/app/results
command: >
mcptesta test
--server "fastmcp-server:8000"
--transport sse
--format junit
--output /app/results/test-results.xml
--parallel 4
```
## Environment-specific configurations
### Development environment tests
Create `tests/development_tests.yaml`:
```yaml
config:
parallel_workers: 2
output_format: "junit"
features:
test_notifications: true
test_progress: true
servers:
- name: "dev_server"
command: "python my_fastmcp_server.py"
transport: "stdio"
env_vars:
ENVIRONMENT: "development"
DEBUG: "true"
test_suites:
- name: "Development Validation"
tests:
- name: "debug_mode_test"
test_type: "tool_call"
target: "debug_info"
- name: "development_features"
test_type: "tool_call"
target: "dev_only_tool"
```
### Staging environment tests
Create `tests/staging_tests.yaml`:
```yaml
config:
parallel_workers: 4
output_format: "junit"
global_timeout: 60
servers:
- name: "staging_server"
command: "${STAGING_SERVER_URL}"
transport: "sse"
headers:
"Authorization": "Bearer ${STAGING_AUTH_TOKEN}"
test_suites:
- name: "Staging Integration"
tests:
- name: "external_api_integration"
test_type: "tool_call"
target: "external_service_call"
timeout: 30
- name: "database_connectivity"
test_type: "resource_read"
target: "db://staging/status"
```
### Production validation tests
Create `tests/production_tests.yaml`:
```yaml
config:
parallel_workers: 2 # Conservative for production
output_format: "junit"
retry_policy:
max_retries: 1
backoff_factor: 2.0
servers:
- name: "production_server"
command: "${PRODUCTION_SERVER_URL}"
transport: "sse"
headers:
"Authorization": "Bearer ${PRODUCTION_AUTH_TOKEN}"
timeout: 30
test_suites:
- name: "Production Health Check"
tests:
- name: "critical_function_test"
test_type: "tool_call"
target: "health_check"
timeout: 10
- name: "performance_validation"
test_type: "tool_call"
target: "lightweight_operation"
timeout: 5
```
## Advanced pipeline patterns
### Deployment gates
Use MCPTesta results to control deployments:
```yaml
# Git workflow deployment gate
deploy:
needs: test
runs-on: ubuntu-latest
if: success() # Only deploy if tests pass
environment: production
steps:
- name: Deploy to production
run: |
echo "Deploying to production..."
# Your deployment commands here
```
### Parallel testing strategies
Run different test types in parallel:
```yaml
jobs:
unit-tests:
runs-on: ubuntu-latest
steps:
- name: Run unit tests
run: mcptesta test --server "python server.py" --include-tools "unit_testable_tools"
integration-tests:
runs-on: ubuntu-latest
steps:
- name: Run integration tests
run: mcptesta yaml integration_tests.yaml
performance-tests:
runs-on: ubuntu-latest
steps:
- name: Run performance tests
run: mcptesta yaml performance_tests.yaml --stress-test
deploy:
needs: [unit-tests, integration-tests, performance-tests]
runs-on: ubuntu-latest
if: success()
steps:
- name: Deploy
run: echo "All tests passed, deploying..."
```
## Monitoring and notifications
### Slack notifications
Add Slack integration to your pipeline:
```yaml
# Git workflow with Slack
- name: Notify Slack on failure
if: failure()
uses: 8398a7/action-slack@v3
with:
status: failure
text: "MCPTesta tests failed for ${{ gitea.repository }}"
webhook_url: ${{ secrets.SLACK_WEBHOOK }}
```
### Email reports
Send detailed test reports via email:
```yaml
- name: Send test report
if: always()
uses: dawidd6/action-send-mail@v3
with:
server_address: smtp.gmail.com
server_port: 587
username: ${{ secrets.EMAIL_USERNAME }}
password: ${{ secrets.EMAIL_PASSWORD }}
subject: "MCPTesta Results: ${{ gitea.repository }}"
body: "Test results attached"
to: team@yourcompany.com
attachments: mcptesta/test-results.xml,mcptesta/performance-report/
```
## Troubleshooting CI/CD issues
### Debug CI failures
Add debugging steps to your pipeline:
```yaml
- name: Debug test failures
if: failure()
run: |
echo "=== System Information ==="
python --version
pip list
echo "=== MCPTesta Debug ==="
cd mcptesta
uv run mcptesta --version
uv run mcptesta yaml ../tests.yaml --dry-run -vv
echo "=== Server Logs ==="
# Add server log collection here
```
### Resource constraints
Handle resource limitations in CI:
```yaml
config:
parallel_workers: 2 # Limit for CI environment
max_concurrent_operations: 4
global_timeout: 120 # Longer timeout for slower CI
test_suites:
- name: "CI-Optimized Tests"
tests:
- name: "lightweight_test"
test_type: "tool_call"
target: "simple_echo"
timeout: 30 # Conservative timeout
```
## Best practices summary
**Start simple**: Begin with basic test integration before adding complex pipelines.
**Use appropriate environments**: Test development changes in dev, staging validation in staging.
**Generate artifacts**: Always save test reports and logs for debugging.
**Gate deployments**: Only deploy when all tests pass.
**Monitor resource usage**: Adjust parallelization for CI environment constraints.
**Handle failures gracefully**: Include debugging information and notification systems.
**Version your tests**: Keep test configurations in version control alongside code.
## What's next?
### **Advanced Integration Patterns**
- **[Container Testing](/how-to/container-testing/)**: Implement containerized CI/CD testing with Docker and Kubernetes
- **[Security Compliance](/how-to/security-compliance/)**: Add security testing and compliance validation to your pipelines
- **[Team Collaboration](/how-to/team-collaboration/)**: Coordinate CI/CD testing across development teams
### **Production Deployment**
- **[Production Testing](/how-to/test-production-servers/)**: Apply CI/CD patterns to production testing scenarios
- **[Troubleshooting](/how-to/troubleshooting/)**: Debug CI/CD-specific testing issues and failures
### **Configuration and Optimization**
- **[YAML Configuration](/tutorials/yaml-configuration/)**: Master CI/CD-optimized configuration patterns
- **[Parallel Testing](/tutorials/parallel-testing/)**: Optimize CI/CD testing performance with intelligent parallelization
### **Foundational Understanding**
- **[Testing Strategies](/explanation/testing-strategies/)**: Learn CI/CD testing methodologies and best practices
- **[Architecture Overview](/explanation/architecture/)**: Understand MCPTesta's CI/CD integration design
### **Reference Materials**
- **[CLI Reference](/reference/cli/)**: CI/CD-specific command-line options and automation features
With these patterns, you can integrate MCPTesta seamlessly into any CI/CD pipeline, ensuring your FastMCP servers are validated automatically with every change.

View File

@ -0,0 +1,657 @@
---
title: Container Testing
description: Test FastMCP servers in Docker containers and Kubernetes environments with production-ready patterns
---
This guide shows you how to test FastMCP servers running in containerized environments, covering Docker containers, Kubernetes deployments, and CI/CD pipeline integration with comprehensive monitoring and debugging strategies.
## Problem scenarios
Use this guide when you need to:
- Test FastMCP servers deployed in Docker containers
- Validate containerized applications in Kubernetes clusters
- Set up testing for multi-container FastMCP architectures
- Debug container networking and communication issues
- Implement testing in containerized CI/CD pipelines
- Monitor FastMCP server performance in container environments
## Prerequisites
- Docker installed and running
- Basic understanding of containerization concepts
- MCPTesta installed (see [Installation](/installation/))
- Familiarity with FastMCP server development
- Access to container orchestration platform (optional for Kubernetes scenarios)
## Docker Container Testing
### Basic container testing setup
Start by creating a comprehensive Docker testing environment:
```yaml
# docker-testing.yaml - Container-focused MCPTesta configuration
config:
parallel_workers: 2
output_format: "html"
output_directory: "./container-test-results"
global_timeout: 120
variables:
# Container configuration
CONTAINER_NAME: "fastmcp-test-server"
CONTAINER_PORT: "8080"
NETWORK_NAME: "fastmcp-test-network"
# Server configuration for container
SERVER_IMAGE: "python:3.11-slim"
SERVER_COMMAND: "python /app/server.py"
# Testing configuration
CONTAINER_STARTUP_WAIT: "10"
HEALTH_CHECK_INTERVAL: "5"
servers:
- name: "containerized_server"
command: "docker exec ${CONTAINER_NAME} ${SERVER_COMMAND}"
transport: "stdio"
timeout: 30
# Container-specific environment
env_vars:
CONTAINER_ENV: "testing"
LOG_LEVEL: "DEBUG"
BIND_HOST: "0.0.0.0"
PORT: "${CONTAINER_PORT}"
test_suites:
- name: "Container Lifecycle Testing"
description: "Test FastMCP server behavior in containerized environment"
setup_commands:
- "docker network create ${NETWORK_NAME} || true"
- "docker build -t fastmcp-test ."
- "docker run -d --name ${CONTAINER_NAME} --network ${NETWORK_NAME} -p ${CONTAINER_PORT}:${CONTAINER_PORT} fastmcp-test"
- "sleep ${CONTAINER_STARTUP_WAIT}"
teardown_commands:
- "docker stop ${CONTAINER_NAME} || true"
- "docker rm ${CONTAINER_NAME} || true"
- "docker network rm ${NETWORK_NAME} || true"
tests:
- name: "container_health_check"
test_type: "ping"
timeout: 15
retry_count: 3
- name: "container_tool_execution"
test_type: "tool_call"
target: "echo"
parameters:
message: "Testing from container environment"
expected:
result: "Echo: Testing from container environment"
timeout: 20
- name: "container_resource_access"
test_type: "resource_read"
target: "system_info"
expected:
content_contains: ["container", "linux"]
timeout: 15
- name: "Container Performance Testing"
description: "Validate performance characteristics in containerized environment"
parallel: true
tests:
- name: "memory_usage_test"
test_type: "tool_call"
target: "memory_info"
expected:
result_type: "object"
timeout: 10
- name: "concurrent_request_handling"
test_type: "tool_call"
target: "echo"
parameters:
message: "Concurrent test ${TEST_ID}"
parallel_instances: 5
timeout: 30
- name: "container_networking_test"
test_type: "tool_call"
target: "network_info"
expected:
result_contains: ["${NETWORK_NAME}"]
timeout: 15
```
### Advanced container testing patterns
For complex containerized applications, use multi-stage testing:
```yaml
# multi-container-testing.yaml - Advanced container orchestration testing
config:
parallel_workers: 3
output_format: "junit"
features:
test_notifications: true
test_progress: true
variables:
COMPOSE_PROJECT: "mcptesta"
REDIS_HOST: "redis"
DATABASE_HOST: "postgres"
FASTMCP_HOST: "fastmcp-server"
servers:
- name: "orchestrated_server"
command: "docker compose exec fastmcp-server python -m fastmcp_server"
transport: "stdio"
timeout: 45
# Multi-container environment variables
env_vars:
REDIS_URL: "redis://${REDIS_HOST}:6379"
DATABASE_URL: "postgresql://user:pass@${DATABASE_HOST}:5432/testdb"
SERVICE_DISCOVERY: "docker"
test_suites:
- name: "Multi-Container Integration"
description: "Test FastMCP server with dependent services"
setup_commands:
- "docker compose -p ${COMPOSE_PROJECT} up -d"
- "docker compose -p ${COMPOSE_PROJECT} exec fastmcp-server python -c 'import redis; r = redis.Redis(host=\"redis\"); r.ping()'"
- "sleep 15" # Allow services to fully initialize
teardown_commands:
- "docker compose -p ${COMPOSE_PROJECT} down -v"
- "docker compose -p ${COMPOSE_PROJECT} rm -f"
tests:
- name: "service_connectivity"
test_type: "tool_call"
target: "health_check"
expected:
result_contains: ["redis", "database", "healthy"]
timeout: 20
- name: "data_persistence_test"
test_type: "tool_call"
target: "store_data"
parameters:
key: "test_key"
value: "containerized_data"
depends_on: ["service_connectivity"]
timeout: 15
- name: "data_retrieval_test"
test_type: "tool_call"
target: "retrieve_data"
parameters:
key: "test_key"
expected:
result: "containerized_data"
depends_on: ["data_persistence_test"]
timeout: 15
```
## Kubernetes Testing
### Pod-level testing
Test FastMCP servers deployed as Kubernetes pods:
```yaml
# kubernetes-testing.yaml - K8s deployment testing
config:
parallel_workers: 1 # Sequential for K8s resource management
output_format: "console"
global_timeout: 180
variables:
NAMESPACE: "mcptesta"
POD_NAME: "fastmcp-test-pod"
SERVICE_NAME: "fastmcp-service"
KUBECTL_CONTEXT: "minikube" # Or your cluster context
servers:
- name: "kubernetes_server"
command: "kubectl exec -n ${NAMESPACE} ${POD_NAME} -- python -m fastmcp_server"
transport: "stdio"
timeout: 60
env_vars:
KUBECONFIG: "${HOME}/.kube/config"
KUBECTL_CONTEXT: "${KUBECTL_CONTEXT}"
test_suites:
- name: "Kubernetes Deployment Testing"
description: "Test FastMCP server in Kubernetes environment"
setup_commands:
- "kubectl create namespace ${NAMESPACE} || true"
- "kubectl apply -f k8s-manifests/ -n ${NAMESPACE}"
- "kubectl wait --for=condition=Ready pod/${POD_NAME} -n ${NAMESPACE} --timeout=120s"
- "kubectl get pods -n ${NAMESPACE}"
teardown_commands:
- "kubectl delete -f k8s-manifests/ -n ${NAMESPACE} || true"
- "kubectl delete namespace ${NAMESPACE} || true"
tests:
- name: "pod_readiness_check"
test_type: "ping"
timeout: 30
retry_count: 5
- name: "service_discovery_test"
test_type: "tool_call"
target: "discover_services"
expected:
result_contains: ["kubernetes", "service"]
timeout: 25
- name: "persistent_volume_test"
test_type: "tool_call"
target: "write_file"
parameters:
path: "/data/test.txt"
content: "Kubernetes persistent data"
timeout: 20
- name: "configmap_access_test"
test_type: "tool_call"
target: "read_config"
parameters:
config_key: "application.yaml"
expected:
result_type: "string"
timeout: 15
```
### Helm chart testing
For Helm-managed deployments:
```bash
# helm-test-runner.sh - Automated Helm chart testing
#!/bin/bash
set -euo pipefail
CHART_PATH="./helm/fastmcp"
RELEASE_NAME="mcptesta-${RANDOM}"
NAMESPACE="mcptesta-test"
VALUES_FILE="./test-values.yaml"
# Function to cleanup on exit
cleanup() {
echo "Cleaning up Helm release..."
helm uninstall "$RELEASE_NAME" -n "$NAMESPACE" || true
kubectl delete namespace "$NAMESPACE" || true
}
trap cleanup EXIT
# Deploy with Helm
echo "Installing Helm chart..."
kubectl create namespace "$NAMESPACE" || true
helm install "$RELEASE_NAME" "$CHART_PATH" \
-n "$NAMESPACE" \
-f "$VALUES_FILE" \
--wait --timeout=300s
# Wait for pods to be ready
echo "Waiting for pods to be ready..."
kubectl wait --for=condition=Ready pods -l app=fastmcp -n "$NAMESPACE" --timeout=180s
# Run MCPTesta against the deployed service
echo "Running MCPTesta against Helm deployment..."
mcptesta yaml helm-deployment-tests.yaml \
--override "variables.NAMESPACE=${NAMESPACE}" \
--override "variables.SERVICE_NAME=${RELEASE_NAME}-fastmcp" \
--output ./helm-test-results \
--format html
echo "Helm chart testing completed successfully!"
```
## CI/CD Pipeline Integration
### Docker-based CI testing
Integrate container testing into Git workflows:
```yaml
# .gitea/workflows/container-testing.yml
name: Container Testing
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
container-tests:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.11", "3.12"]
container-runtime: ["docker", "podman"]
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install mcptesta
- name: Build test container
run: |
docker build -t fastmcp-test:${{ gitea.sha }} .
- name: Run container tests
run: |
mcptesta yaml .mcptesta/container-tests.yaml \
--override "variables.SERVER_IMAGE=fastmcp-test:${{ gitea.sha }}" \
--override "variables.CONTAINER_RUNTIME=${{ matrix.container-runtime }}" \
--output ./test-results \
--format junit
- name: Upload test results
uses: actions/upload-artifact@v3
if: always()
with:
name: container-test-results-${{ matrix.python-version }}-${{ matrix.container-runtime }}
path: ./test-results/
- name: Publish test results
uses: dorny/test-reporter@v1
if: always()
with:
name: Container Tests (${{ matrix.python-version }}, ${{ matrix.container-runtime }})
path: './test-results/*.xml'
reporter: java-junit
```
### GitLab CI container pipeline
```yaml
# .gitlab-ci.yml - Container testing pipeline
stages:
- build
- test-unit
- test-container
- test-integration
variables:
DOCKER_REGISTRY: $CI_REGISTRY
IMAGE_NAME: $CI_REGISTRY_IMAGE/fastmcp-server
MCPTESTA_VERSION: "latest"
build-container:
stage: build
image: docker:24
services:
- docker:24-dind
script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
- docker build -t $IMAGE_NAME:$CI_COMMIT_SHA .
- docker push $IMAGE_NAME:$CI_COMMIT_SHA
only:
- main
- develop
- merge_requests
container-functionality-tests:
stage: test-container
image: python:3.11
services:
- docker:24-dind
variables:
DOCKER_HOST: tcp://docker:2376
DOCKER_TLS_CERTDIR: "/certs"
before_script:
- pip install mcptesta
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
- docker pull $IMAGE_NAME:$CI_COMMIT_SHA
script:
- mcptesta yaml tests/container-tests.yaml
--override "variables.SERVER_IMAGE=$IMAGE_NAME:$CI_COMMIT_SHA"
--format junit
--output container-test-results
artifacts:
reports:
junit: container-test-results/*.xml
paths:
- container-test-results/
expire_in: 1 week
coverage: '/TOTAL.*\s+(\d+%)$/'
container-performance-tests:
stage: test-container
image: python:3.11
services:
- docker:24-dind
script:
- mcptesta yaml tests/performance-tests.yaml
--override "variables.SERVER_IMAGE=$IMAGE_NAME:$CI_COMMIT_SHA"
--parallel 4
--stress-test
--output performance-results
artifacts:
paths:
- performance-results/
only:
- schedules
- main
```
## Debugging Container Issues
### Container log analysis
When tests fail, analyze container logs systematically:
```bash
# container-debug.sh - Container debugging utilities
#!/bin/bash
CONTAINER_NAME="${1:-fastmcp-test-server}"
LOG_DIR="./debug-logs"
mkdir -p "$LOG_DIR"
echo "Collecting container debug information..."
# Container status and configuration
docker inspect "$CONTAINER_NAME" > "$LOG_DIR/container-inspect.json"
docker logs "$CONTAINER_NAME" > "$LOG_DIR/container-logs.txt" 2>&1
# Network configuration
docker network ls > "$LOG_DIR/networks.txt"
docker exec "$CONTAINER_NAME" ip addr show > "$LOG_DIR/container-network.txt" 2>&1
# Process and resource information
docker exec "$CONTAINER_NAME" ps aux > "$LOG_DIR/container-processes.txt" 2>&1
docker exec "$CONTAINER_NAME" df -h > "$LOG_DIR/container-disk.txt" 2>&1
docker exec "$CONTAINER_NAME" free -h > "$LOG_DIR/container-memory.txt" 2>&1
# Application-specific debugging
docker exec "$CONTAINER_NAME" python -c "import sys; print('Python:', sys.version)" > "$LOG_DIR/python-version.txt" 2>&1
docker exec "$CONTAINER_NAME" pip list > "$LOG_DIR/installed-packages.txt" 2>&1
echo "Debug information collected in $LOG_DIR/"
echo "Run MCPTesta with --debug for additional information"
```
### Network connectivity troubleshooting
```yaml
# network-debug-tests.yaml - Network troubleshooting configuration
config:
parallel_workers: 1
output_format: "console"
debug: true
variables:
CONTAINER_NAME: "fastmcp-debug"
NETWORK_NAME: "fastmcp-debug-net"
servers:
- name: "debug_server"
command: "docker exec ${CONTAINER_NAME} python -m fastmcp_server --debug"
transport: "stdio"
timeout: 60
test_suites:
- name: "Network Connectivity Debugging"
description: "Diagnose container networking issues"
setup_commands:
- "docker network create ${NETWORK_NAME} || true"
- "docker run -d --name ${CONTAINER_NAME} --network ${NETWORK_NAME} fastmcp-test"
- "sleep 10"
tests:
- name: "container_network_check"
test_type: "tool_call"
target: "network_test"
parameters:
target_host: "host.docker.internal"
port: 22
timeout: 30
- name: "dns_resolution_test"
test_type: "tool_call"
target: "dns_lookup"
parameters:
hostname: "google.com"
timeout: 20
- name: "port_binding_test"
test_type: "tool_call"
target: "port_check"
parameters:
port: 8080
timeout: 15
```
## Performance Optimization
### Container resource monitoring
Monitor FastMCP server performance in containers:
```yaml
# performance-monitoring.yaml - Container performance testing
config:
parallel_workers: 4
output_format: "html"
features:
performance_monitoring: true
resource_tracking: true
variables:
MEMORY_LIMIT: "512m"
CPU_LIMIT: "1.0"
MONITORING_INTERVAL: "5"
servers:
- name: "monitored_server"
command: "docker exec fastmcp-monitored python -m fastmcp_server"
transport: "stdio"
timeout: 30
resource_limits:
memory: "${MEMORY_LIMIT}"
cpu: "${CPU_LIMIT}"
test_suites:
- name: "Resource Usage Monitoring"
description: "Monitor container resource consumption during testing"
setup_commands:
- "docker run -d --name fastmcp-monitored --memory=${MEMORY_LIMIT} --cpus=${CPU_LIMIT} fastmcp-test"
- "sleep 10"
monitoring:
enabled: true
interval: "${MONITORING_INTERVAL}"
metrics: ["cpu", "memory", "network", "disk"]
tests:
- name: "baseline_performance"
test_type: "tool_call"
target: "simple_operation"
performance_baseline: true
timeout: 10
- name: "load_test"
test_type: "tool_call"
target: "cpu_intensive_operation"
parallel_instances: 10
timeout: 60
- name: "memory_stress_test"
test_type: "tool_call"
target: "memory_allocation"
parameters:
size_mb: 100
timeout: 30
```
## Best Practices Summary
### Container testing principles
1. **Isolation**: Each test should start with a clean container environment
2. **Reproducibility**: Use fixed image tags and explicit configuration
3. **Resource Management**: Set appropriate limits and cleanup procedures
4. **Monitoring**: Track performance and resource usage
5. **Security**: Test with minimal required permissions
### Debugging strategies
1. **Comprehensive Logging**: Capture container, application, and system logs
2. **Network Analysis**: Verify connectivity and DNS resolution
3. **Resource Monitoring**: Check CPU, memory, and disk usage
4. **Progressive Testing**: Start simple, add complexity gradually
### Performance considerations
1. **Container Overhead**: Account for containerization performance impact
2. **Resource Constraints**: Test within realistic production limits
3. **Scaling Patterns**: Validate horizontal and vertical scaling
4. **Health Monitoring**: Implement comprehensive health checks
## What's next?
### **Related How-to Guides**
- **[CI/CD Integration](/how-to/ci-cd-integration/)**: Integrate container testing into automated deployment pipelines
- **[Team Collaboration](/how-to/team-collaboration/)**: Implement shared container testing workflows across teams
- **[Security Compliance](/how-to/security-compliance/)**: Apply security testing to containerized FastMCP servers
- **[Production Testing](/how-to/test-production-servers/)**: Apply container testing strategies to production environments
### **Foundational Learning**
- **[Parallel Testing](/tutorials/parallel-testing/)**: Optimize container testing performance with parallelization
- **[YAML Configuration](/tutorials/yaml-configuration/)**: Master complex configuration patterns for container environments
### **Advanced Concepts**
- **[Architecture Overview](/explanation/architecture/)**: Understand how MCPTesta handles containerized environments
- **[Testing Strategies](/explanation/testing-strategies/)**: Learn containerization-specific testing methodologies
### **Reference Materials**
- **[CLI Reference](/reference/cli/)**: Container-specific command-line options and flags
- **[Troubleshooting](/how-to/troubleshooting/)**: Debug complex container-specific issues

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,522 @@
---
title: Troubleshooting
description: Common issues and solutions when using MCPTesta with FastMCP servers
---
This guide helps you diagnose and resolve common issues when testing FastMCP servers with MCPTesta.
## Problem scenarios
Use this guide when you encounter:
- Connection failures or timeouts
- Test configuration errors
- Server discovery issues
- Performance problems
- Authentication failures
- YAML parsing errors
## Quick diagnostics
### Check MCPTesta installation
Verify your installation is working:
```bash
# Check version and basic functionality
mcptesta --version
# Test with verbose output
mcptesta ping --server "python -c 'print(\"test\")'" -vv
```
### Validate server connectivity
Test basic server connection:
```bash
# Basic connectivity test
mcptesta ping --server "your-server-command" --count 5
# Detailed server validation
mcptesta validate --server "your-server-command" -vv
```
### Check configuration syntax
Validate YAML configurations before running:
```bash
# Dry run to check syntax
mcptesta yaml your_config.yaml --dry-run
# List tests to verify parsing
mcptesta yaml your_config.yaml --list-tests
```
## Common connection issues
### Server startup failures
**Problem**: Server won't start or immediately exits
**Diagnosis**:
```bash
# Test server startup manually
python your_fastmcp_server.py
# Check for import errors
python -c "import your_fastmcp_server"
# Verify FastMCP installation
python -c "import fastmcp; print(fastmcp.__version__)"
```
**Solutions**:
- Ensure all dependencies are installed: `uv sync` or `pip install -r requirements.txt`
- Check Python path and virtual environment activation
- Verify FastMCP server code has no syntax errors
- Check for missing environment variables
### Connection timeouts
**Problem**: Tests fail with connection timeout errors
**Configuration for debugging**:
```yaml
config:
parallel_workers: 1 # Reduce concurrency
global_timeout: 60 # Increase timeout
servers:
- name: "debug_server"
command: "your-server-command"
transport: "stdio"
timeout: 30 # Increase server timeout
test_suites:
- name: "Timeout Debugging"
tests:
- name: "simple_ping"
test_type: "ping"
timeout: 10 # Start with longer timeout
```
**Solutions**:
- Increase timeout values gradually
- Check server startup time with manual testing
- Verify server isn't blocked by other processes
- Test with reduced parallelization
- Check system resource availability
### Transport protocol issues
**Problem**: Issues with specific transport types (stdio, sse, ws)
**Test different transports**:
```bash
# Test stdio transport (default)
mcptesta test --server "python server.py" --transport stdio
# Test SSE transport
mcptesta test --server "http://localhost:8080/mcp" --transport sse
# Test WebSocket transport
mcptesta test --server "ws://localhost:8081/mcp" --transport ws
```
**Common fixes**:
- **stdio**: Ensure server accepts stdin/stdout communication
- **sse**: Verify HTTP server is running and accessible
- **ws**: Check WebSocket endpoint is available and not blocked by firewalls
## Configuration errors
### YAML syntax errors
**Problem**: YAML parsing fails with syntax errors
**Common syntax issues**:
```yaml
# ❌ Incorrect indentation
config:
parallel_workers: 4 # Missing indentation
# ✅ Correct indentation
config:
parallel_workers: 4
# ❌ Missing quotes for special characters
message: Hello: World # Colon needs quotes
# ✅ Proper quoting
message: "Hello: World"
# ❌ List syntax error
tags: [tag1, tag2 # Missing closing bracket
# ✅ Correct list syntax
tags: ["tag1", "tag2"]
```
**Debugging tools**:
```bash
# Use a YAML validator
python -c "import yaml; yaml.safe_load(open('your_config.yaml'))"
# MCPTesta syntax check
mcptesta yaml your_config.yaml --dry-run
```
### Variable substitution issues
**Problem**: Variables not resolving correctly
**Debug variable resolution**:
```yaml
variables:
TEST_VAR: "test_value"
MISSING_VAR: "${UNDEFINED_VAR:default_value}"
config:
# Debug by echoing variables
output_directory: "./debug_${TEST_VAR}"
test_suites:
- name: "Variable Debug"
tests:
- name: "echo_variable"
test_type: "tool_call"
target: "echo"
parameters:
message: "Variable value: ${TEST_VAR}"
```
**Solutions**:
- Check variable names for typos
- Ensure variables are defined before use
- Use default values: `${VAR:default_value}`
- Check environment variable availability
### Dependency resolution errors
**Problem**: Tests fail due to dependency issues
**Debug dependency chains**:
```bash
# List tests to see execution order
mcptesta yaml config.yaml --list-tests
# Run with dependency debug info
mcptesta yaml config.yaml -vv
```
**Common dependency issues**:
```yaml
test_suites:
- name: "Dependency Debug"
tests:
- name: "test_a"
test_type: "ping"
- name: "test_b"
test_type: "tool_call"
target: "echo"
depends_on: ["test_a"] # ✅ Correct reference
- name: "test_c"
test_type: "tool_call"
target: "echo"
depends_on: ["typo_test"] # ❌ Non-existent dependency
```
## Server discovery issues
### Tools not found
**Problem**: MCPTesta can't find expected tools
**Debug tool discovery**:
```bash
# Validate server and list capabilities
mcptesta validate --server "your-server-command"
# Test specific tool existence
mcptesta test --server "your-server-command" --include-tools "specific_tool"
```
**Check server implementation**:
```python
from fastmcp import FastMCP
mcp = FastMCP("Debug Server")
@mcp.tool()
def debug_tool() -> str:
"""Debug tool for testing discovery."""
return "Debug successful"
# Verify tool registration
print(f"Registered tools: {list(mcp._tools.keys())}")
if __name__ == "__main__":
mcp.run()
```
### Resource access issues
**Problem**: Resources not accessible or returning errors
**Debug resource access**:
```yaml
test_suites:
- name: "Resource Debug"
tests:
- name: "list_resources"
test_type: "resource_read"
target: "list" # List all available resources
- name: "test_specific_resource"
test_type: "resource_read"
target: "config://server.json"
timeout: 10
```
**Common resource issues**:
- File path errors (check working directory)
- Permission issues (file system access)
- Network connectivity (for HTTP resources)
- Resource URI format errors
## Performance issues
### Slow test execution
**Problem**: Tests take much longer than expected
**Performance debugging configuration**:
```yaml
config:
parallel_workers: 1 # Isolate performance issues
enable_performance_profiling: true
enable_memory_profiling: true
output_format: "html" # Get detailed performance reports
test_suites:
- name: "Performance Debug"
tests:
- name: "baseline_test"
test_type: "ping"
timeout: 5
- name: "slow_operation"
test_type: "tool_call"
target: "your_slow_tool"
timeout: 60
```
**Analysis steps**:
1. Run with performance profiling enabled
2. Check HTML report for timing breakdown
3. Identify bottlenecks in server response times
4. Optimize server code or increase timeouts
### Memory issues
**Problem**: Tests fail with memory errors or system becomes unresponsive
**Memory-safe configuration**:
```yaml
config:
parallel_workers: 2 # Reduce parallelization
max_concurrent_operations: 4
enable_memory_profiling: true
test_suites:
- name: "Memory-Safe Tests"
tests:
- name: "small_payload_test"
test_type: "tool_call"
target: "echo"
parameters:
message: "small message" # Start with small data
```
**Solutions**:
- Reduce parallel worker count
- Limit concurrent operations
- Check for memory leaks in server code
- Use smaller test payloads
- Monitor system memory usage during tests
## Authentication failures
### Token authentication issues
**Problem**: Tests fail with authentication errors
**Debug authentication**:
```bash
# Test with explicit authentication
export AUTH_TOKEN="your-token"
mcptesta test --server "your-server" --auth-token "$AUTH_TOKEN"
# Verify token format and validity
echo $AUTH_TOKEN | base64 -d # For JWT tokens
```
**Common auth issues**:
- Expired tokens
- Incorrect token format
- Missing authentication headers
- Server-side authentication configuration
### OAuth flow problems
**Problem**: OAuth authentication fails
**Debug OAuth configuration**:
```yaml
servers:
- name: "oauth_debug"
command: "your-oauth-server"
transport: "sse"
auth_config:
type: "oauth"
client_id: "${OAUTH_CLIENT_ID}"
client_secret: "${OAUTH_CLIENT_SECRET}"
token_url: "${OAUTH_TOKEN_URL}"
# Add debug flags
debug_auth: true
```
**Troubleshooting steps**:
1. Verify OAuth credentials are correct
2. Check token endpoint accessibility
3. Validate OAuth scopes and permissions
4. Test OAuth flow manually outside MCPTesta
## Advanced debugging
### Enable detailed logging
**Maximum verbosity configuration**:
```bash
# CLI with maximum debugging
mcptesta test --server "your-server" -vv --debug
# YAML with detailed logging
mcptesta yaml config.yaml -vv
```
**Add logging to your configuration**:
```yaml
config:
logging:
level: "DEBUG"
console_output: true
file_output: "./debug.log"
rich_tracebacks: true
```
### Network debugging
**Problem**: Network connectivity issues
**Network diagnostic tests**:
```bash
# Test network connectivity
ping your-server-host
telnet your-server-host 8080
# Test HTTP endpoints
curl -v http://your-server:8080/health
# Test WebSocket endpoints
websocat ws://your-server:8081/mcp
```
### Environment debugging
**Problem**: Tests work locally but fail in CI/CD
**Environment comparison checklist**:
```bash
# Compare Python versions
python --version
# Compare installed packages
pip list | grep fastmcp
pip list | grep mcptesta
# Compare environment variables
env | grep -E "(PYTHON|PATH|HOME)"
# Check system resources
free -h
ps aux | grep python
```
## Error message guide
### Common error patterns
**"Connection refused"**:
- Server not running or wrong port
- Firewall blocking connection
- Server crashed during startup
**"Timeout waiting for response"**:
- Server too slow to respond
- Network latency issues
- Server hung or deadlocked
**"Tool not found"**:
- Tool not registered in FastMCP server
- Tool name mismatch
- Server capability discovery failed
**"Invalid parameters"**:
- Parameter type mismatch
- Missing required parameters
- Extra parameters not accepted
**"Authentication failed"**:
- Invalid or expired credentials
- Wrong authentication method
- Server-side auth configuration issue
## Getting help
### Collect diagnostic information
Before seeking help, gather:
```bash
# System information
mcptesta --version
python --version
pip list | grep -E "(fastmcp|mcptesta)"
# Configuration and logs
mcptesta yaml your_config.yaml --dry-run -vv > debug_output.txt
# Server information
mcptesta validate --server "your-server" -vv
```
### Where to get help
- **Git Issues**: [MCPTesta Issues](https://git.supported.systems/mcp/mcptesta/issues)
- **Discussions**: [MCPTesta Discussions](https://git.supported.systems/mcp/mcptesta/discussions)
- **FastMCP Issues**: [FastMCP Repository](https://github.com/jlowin/fastmcp)
### Creating good bug reports
Include:
1. **Environment details**: OS, Python version, MCPTesta version
2. **Minimal reproduction**: Simplest configuration that shows the problem
3. **Expected vs actual behavior**: What you expected and what happened
4. **Logs**: Complete error output with `-vv` flag
5. **Server code**: Minimal FastMCP server that reproduces the issue
Most issues can be resolved quickly with systematic debugging and the right diagnostic information.

View File

@ -0,0 +1,118 @@
---
title: Welcome to MCPTesta 🧪✨
description: Comprehensive testing framework for FastMCP servers
template: splash
hero:
tagline: Community-driven testing excellence for the MCP ecosystem
image:
file: ../../assets/mcptesta-logo.svg
actions:
- text: Get Started
link: /tutorials/first-test/
icon: right-arrow
variant: primary
- text: View Git
link: https://git.supported.systems/mcp/mcptesta
icon: external
---
import { Card, CardGrid } from '@astrojs/starlight/components';
## Why MCPTesta?
MCPTesta brings enterprise-grade testing capabilities to the FastMCP ecosystem. Whether you're building your first FastMCP server or orchestrating complex multi-server architectures, MCPTesta provides the tools you need.
<CardGrid stagger>
<Card title="🎯 Comprehensive Testing" icon="seti:config">
Test every aspect of your FastMCP servers: connectivity, tools, resources, prompts, and advanced protocol features like notifications and cancellation.
</Card>
<Card title="⚡ Parallel Execution" icon="seti:javascript">
Intelligent dependency resolution and workload distribution across multiple workers for lightning-fast test execution.
</Card>
<Card title="📊 Rich Reporting" icon="seti:json">
Beautiful console output, comprehensive HTML reports, JUnit XML for CI/CD, and JSON for programmatic analysis.
</Card>
<Card title="🔧 Flexible Configuration" icon="seti:yaml">
From simple CLI commands to sophisticated YAML configurations with variables, dependencies, and template inheritance.
</Card>
</CardGrid>
## Core Capabilities
### Advanced MCP Protocol Testing
- **Notification System**: Resource/tool/prompt list change detection
- **Progress Reporting**: Real-time operation monitoring
- **Request Cancellation**: Graceful operation termination
- **Sampling Mechanisms**: Configurable request throttling
### Enterprise Features
- **Authentication Testing**: Bearer tokens, OAuth flows
- **Stress Testing**: Load simulation and performance validation
- **CI/CD Integration**: JUnit output, parallel execution
- **Multi-Server Testing**: Load balancing across server instances
### Developer Experience
- **Zero-Config Start**: `mcptesta test --server "python -m my_server"`
- **Rich CLI**: Interactive progress indicators and colored output
- **Template System**: 6-tier YAML templates from Basic to Expert
- **Session Management**: Connection pooling and lifecycle management
## Quick Examples
### CLI Testing
```bash
# Basic connectivity test
mcptesta test --server "python -m my_fastmcp_server"
# Advanced feature testing
mcptesta test \
--server "uvx my-mcp-server" \
--test-notifications \
--test-cancellation \
--test-progress \
--parallel 4
```
### YAML Configuration
```yaml
config:
parallel_workers: 8
features:
test_notifications: true
test_cancellation: true
test_progress: true
servers:
- name: "primary"
command: "python -m my_fastmcp_server"
transport: "stdio"
test_suites:
- name: "Core Tests"
tests:
- name: "connectivity"
test_type: "ping"
- name: "tool_validation"
test_type: "tool_call"
target: "echo"
parameters: {message: "Hello MCPTesta!"}
```
## What's Next?
<CardGrid>
<Card title="🚀 First Test" icon="rocket">
New to MCPTesta? Start with our [First Test Tutorial](/tutorials/first-test/) to test your FastMCP server in minutes.
</Card>
<Card title="📖 Complete Walkthrough" icon="document">
Explore all capabilities with our [Testing Walkthrough](/tutorials/testing-walkthrough/) tutorial.
</Card>
<Card title="🔧 Configuration Deep Dive" icon="setting">
Master YAML configurations with our [Configuration Guide](/tutorials/yaml-configuration/).
</Card>
<Card title="⚡ Parallel Testing" icon="lightning">
Scale your testing with [Parallel Execution](/tutorials/parallel-testing/) strategies.
</Card>
</CardGrid>
Ready to revolutionize your FastMCP testing? Let's get started!

View File

@ -0,0 +1,142 @@
---
title: Installation
description: Step-by-step installation guide for MCPTesta with multiple installation methods
---
MCPTesta requires Python 3.11+ and works best with `uv` for dependency management.
## Requirements
- **Python 3.11 or higher**
- **FastMCP server** to test (any FastMCP-compatible server)
- **uv** (recommended) or pip for installation
## Quick Installation
### Using uv (Recommended)
```bash
# Clone the repository
git clone https://git.supported.systems/mcp/mcptesta.git
cd mcptesta
# Install dependencies and activate environment
uv sync
# Verify installation
uv run mcptesta --version
```
### Using pip
```bash
# Clone the repository
git clone https://git.supported.systems/mcp/mcptesta.git
cd mcptesta
# Create virtual environment
python -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activate
# Install dependencies
pip install -e .
# Verify installation
mcptesta --version
```
## Development Installation
For development work or contributing to MCPTesta:
```bash
# Clone and install with development dependencies
git clone https://git.supported.systems/mcp/mcptesta.git
cd mcptesta
uv sync --dev
# Run tests to verify installation
uv run pytest
# Check code formatting
uv run black --check .
uv run ruff check .
```
## System Dependencies
MCPTesta has minimal system dependencies. All required Python packages are automatically installed during setup:
- **click**: CLI interface
- **rich**: Enhanced console output
- **pydantic**: Configuration validation
- **pyyaml**: YAML parsing
- **fastmcp**: MCP client functionality
- **pytest**: Testing framework (dev only)
## Verification
Test your installation with a simple ping test:
```bash
# Test with a FastMCP server command
mcptesta ping --server "python -m your_fastmcp_server"
# Or use the echo server for testing
mcptesta ping --server "python -c 'import fastmcp; fastmcp.echo_server()'"
```
If you see successful ping results, MCPTesta is ready to use!
## Troubleshooting
### Common Issues
**Import Error**: If you get import errors, ensure you're in the correct virtual environment:
```bash
# With uv
uv run mcptesta --version
# With pip/venv
source venv/bin/activate
mcptesta --version
```
**Command Not Found**: If `mcptesta` command is not found:
```bash
# Check if you're in the project directory
pwd
# Reinstall in development mode
uv sync # or pip install -e .
```
**Permission Issues**: On some systems, you may need to adjust permissions:
```bash
# Make sure scripts are executable
chmod +x scripts/*
```
### Platform-Specific Notes
**Windows**: Use `uv run` prefix for all commands or activate the virtual environment manually.
**macOS**: No special considerations, works with both Intel and Apple Silicon.
**Linux**: Works on all major distributions. Some minimal distributions may need additional packages.
## Next Steps
With MCPTesta installed, you're ready to:
- [Write your first test](/tutorials/first-test/) - Learn the basics
- [Explore testing capabilities](/tutorials/testing-walkthrough/) - See what's possible
- [Set up CI/CD testing](/how-to/ci-cd-integration/) - Automate your testing
## Getting Help
If you encounter installation issues:
- Check our [FAQ](/how-to/troubleshooting/)
- Review [Git Issues](https://git.supported.systems/mcp/mcptesta/issues)
- Ask questions in [Git Discussions](https://git.supported.systems/mcp/mcptesta/discussions)

View File

@ -0,0 +1,78 @@
---
title: Introduction
description: Getting started with MCPTesta - comprehensive testing framework for FastMCP servers
---
MCPTesta is a comprehensive testing framework for FastMCP servers that brings enterprise-grade testing capabilities to the MCP ecosystem. Whether you're a beginner getting started with FastMCP testing or an expert building complex testing suites, MCPTesta provides the tools you need.
## What is MCPTesta?
MCPTesta is designed to test every aspect of FastMCP servers:
- **🎯 Core Testing**: Connectivity, tools, resources, and prompts
- **🚀 Advanced Protocol Features**: Notifications, cancellation, progress reporting, sampling
- **⚡ Parallel Execution**: Intelligent dependency resolution and workload distribution
- **📊 Rich Reporting**: Console, HTML, JSON, and JUnit output formats
- **🔧 Flexible Configuration**: CLI parameters and comprehensive YAML configurations
## Who is this for?
### FastMCP Beginners
If you're new to FastMCP, our tutorials will guide you through your first tests and help you understand MCP protocol concepts through hands-on experience.
### FastMCP Developers
If you're building FastMCP servers, our how-to guides provide practical solutions for testing specific scenarios, from basic connectivity to complex workflow validation.
### Technical Teams
If you're integrating MCPTesta into CI/CD pipelines or building enterprise testing infrastructure, our reference documentation provides complete API and configuration details.
### Architects and Technical Leads
If you want to understand the deeper concepts behind MCP protocol testing and MCPTesta's architectural decisions, our explanations provide the bigger picture.
## Quick Start
Ready to test your first FastMCP server? Choose your path:
- **New to FastMCP testing?** → Start with [Your First Test Tutorial](/tutorials/first-test/)
- **Need to solve a specific problem?** → Browse our [How-to Guides](/how-to/)
- **Looking for specific configuration options?** → Check the [Reference](/reference/)
- **Want to understand the concepts?** → Read our [Explanations](/explanation/)
## Core Capabilities at a Glance
MCPTesta handles the complexity of MCP protocol testing so you can focus on building great FastMCP servers:
### Testing Types
- **Connectivity Testing**: Ping, validation, capability discovery
- **Tool Testing**: Parameter validation, response verification, error handling
- **Resource Testing**: Content validation, access control, caching
- **Prompt Testing**: Template rendering, argument processing
- **Notification Testing**: List change monitoring, real-time updates
### Advanced Features
- **Progress Monitoring**: Real-time operation tracking
- **Request Cancellation**: Graceful operation termination
- **Sampling Mechanisms**: Configurable request throttling
- **Authentication Testing**: Bearer tokens, OAuth flows
- **Stress Testing**: Load simulation and performance validation
### Configuration Flexibility
```bash
# Simple CLI testing
mcptesta test --server "python -m my_server"
# Advanced YAML configuration
mcptesta yaml comprehensive_tests.yaml --parallel 8
```
MCPTesta grows with your needs, from simple connectivity checks to comprehensive test suites that validate every aspect of your FastMCP server.
## What's Next?
- **Learn by doing** → [Your First Test Tutorial](/tutorials/first-test/)
- **Explore capabilities** → [Testing Walkthrough Tutorial](/tutorials/testing-walkthrough/)
- **Solve specific problems** → [How-to Guides](/how-to/)
- **Master the tools** → [CLI Reference](/reference/cli/) and [YAML Reference](/reference/yaml/)
- **Understand the concepts** → [MCP Protocol Testing](/explanation/mcp-protocol/) and [Architecture Overview](/explanation/architecture/)
Welcome to comprehensive FastMCP server testing. Let's build something great together.

View File

@ -0,0 +1,711 @@
---
title: API Reference
description: Complete Python API reference for programmatic use of MCPTesta
---
Complete reference for MCPTesta's Python API for programmatic usage and extension.
## Core Classes
### MCPTestClient
Main client class for connecting to and testing FastMCP servers.
```python
from mcptesta.core.client import MCPTestClient, ServerConfig
class MCPTestClient:
def __init__(self, config: ServerConfig) -> None
async def __aenter__(self) -> MCPTestClient
async def __aexit__(self, exc_type, exc_val, exc_tb) -> None
async def connect(self) -> None
async def disconnect(self) -> None
async def ping(self, timeout: float = 5.0) -> PingResult
async def call_tool(self, name: str, parameters: Dict[str, Any] = None, **kwargs) -> ToolResult
async def read_resource(self, uri: str, **kwargs) -> ResourceResult
async def get_prompt(self, name: str, arguments: Dict[str, Any] = None, **kwargs) -> PromptResult
async def list_tools(self) -> List[Dict[str, Any]]
async def list_resources(self) -> List[Dict[str, Any]]
async def list_prompts(self) -> List[Dict[str, Any]]
async def get_server_info(self) -> Dict[str, Any]
```
#### Usage Example
```python
import asyncio
from mcptesta.core.client import MCPTestClient, ServerConfig
async def test_server():
config = ServerConfig(
name="my_server",
command="python my_fastmcp_server.py",
transport="stdio",
timeout=30
)
async with MCPTestClient(config) as client:
# Test connectivity
ping_result = await client.ping()
print(f"Ping successful: {ping_result.success}")
# Test tool
tool_result = await client.call_tool("echo", {"message": "Hello"})
print(f"Tool result: {tool_result.response}")
# List capabilities
tools = await client.list_tools()
print(f"Available tools: {[tool['name'] for tool in tools]}")
asyncio.run(test_server())
```
### ServerConfig
Configuration for server connections.
```python
from mcptesta.core.client import ServerConfig, AuthConfig
class ServerConfig:
def __init__(
self,
name: str,
command: str,
transport: str = "stdio",
timeout: int = 30,
working_directory: Optional[str] = None,
env_vars: Optional[Dict[str, str]] = None,
headers: Optional[Dict[str, str]] = None,
auth_config: Optional[AuthConfig] = None,
enabled: bool = True
) -> None
```
#### Transport Types
**stdio**: Process-based communication
```python
config = ServerConfig(
name="stdio_server",
command="python my_server.py",
transport="stdio",
working_directory="/path/to/server",
env_vars={"DEBUG": "1"}
)
```
**sse**: Server-Sent Events over HTTP
```python
config = ServerConfig(
name="sse_server",
command="http://localhost:8080/mcp",
transport="sse",
headers={"Authorization": "Bearer token123"}
)
```
**ws**: WebSocket connections
```python
config = ServerConfig(
name="ws_server",
command="ws://localhost:8081/mcp",
transport="ws",
headers={"Authorization": "Bearer token123"}
)
```
### AuthConfig
Authentication configuration for secure connections.
```python
from mcptesta.core.client import AuthConfig
class AuthConfig:
def __init__(
self,
auth_type: str, # "bearer", "oauth", "basic"
**kwargs
) -> None
# Bearer token authentication
auth = AuthConfig(auth_type="bearer", token="your_token")
# OAuth authentication
auth = AuthConfig(
auth_type="oauth",
client_id="your_client_id",
client_secret="your_client_secret",
token_url="https://auth.example.com/token",
scope="mcp:read mcp:write"
)
# Basic authentication
auth = AuthConfig(
auth_type="basic",
username="user",
password="pass"
)
```
## Result Classes
### TestResult
Base result class for all test operations.
```python
from mcptesta.core.client import TestResult
class TestResult:
def __init__(
self,
test_name: str,
success: bool,
execution_time: float,
response_data: Optional[Dict[str, Any]] = None,
error_message: Optional[str] = None,
metadata: Optional[Dict[str, Any]] = None
) -> None
def to_dict(self) -> Dict[str, Any]
def __str__(self) -> str
```
### PingResult
Result from ping operations.
```python
from mcptesta.core.client import PingResult
class PingResult(TestResult):
def __init__(
self,
success: bool,
latency: float,
error_message: Optional[str] = None
) -> None
@property
def latency_ms(self) -> float # Latency in milliseconds
```
### ToolResult
Result from tool call operations.
```python
from mcptesta.core.client import ToolResult
class ToolResult(TestResult):
def __init__(
self,
tool_name: str,
success: bool,
execution_time: float,
response: Optional[Any] = None,
error_message: Optional[str] = None,
progress_updates: Optional[List[Dict]] = None
) -> None
@property
def response_type(self) -> str
def validate_response(self, expected: Dict[str, Any]) -> bool
```
### ResourceResult
Result from resource read operations.
```python
from mcptesta.core.client import ResourceResult
class ResourceResult(TestResult):
def __init__(
self,
resource_uri: str,
success: bool,
execution_time: float,
content: Optional[Any] = None,
content_type: Optional[str] = None,
content_length: Optional[int] = None,
error_message: Optional[str] = None
) -> None
@property
def content_size_mb(self) -> float
def validate_content(self, expected: Dict[str, Any]) -> bool
```
### PromptResult
Result from prompt generation operations.
```python
from mcptesta.core.client import PromptResult
class PromptResult(TestResult):
def __init__(
self,
prompt_name: str,
success: bool,
execution_time: float,
messages: Optional[List[Dict]] = None,
error_message: Optional[str] = None
) -> None
@property
def message_count(self) -> int
def validate_messages(self, expected: Dict[str, Any]) -> bool
```
## Configuration Classes
### TestConfig
Main configuration class for test execution.
```python
from mcptesta.core.config import TestConfig
class TestConfig:
def __init__(
self,
parallel_workers: int = 1,
output_directory: Optional[str] = None,
output_format: str = "console",
global_timeout: int = 300,
max_concurrent_operations: int = 10,
enable_stress_testing: bool = False,
enable_memory_profiling: bool = False,
enable_performance_profiling: bool = False,
**kwargs
) -> None
@classmethod
def from_cli_args(cls, **kwargs) -> TestConfig
@classmethod
def from_yaml(cls, config_path: str) -> TestConfig
def apply_filters(
self,
name_pattern: Optional[str] = None,
include_tags: Optional[List[str]] = None,
exclude_tags: Optional[List[str]] = None
) -> None
```
### TestCase
Individual test case configuration.
```python
from mcptesta.yaml_parser.parser import TestCase
class TestCase:
def __init__(
self,
name: str,
test_type: str,
target: str,
parameters: Optional[Dict[str, Any]] = None,
expected: Optional[Dict[str, Any]] = None,
expected_error: Optional[str] = None,
timeout: Optional[int] = None,
depends_on: Optional[List[str]] = None,
tags: Optional[List[str]] = None,
enabled: bool = True,
**kwargs
) -> None
def validate(self) -> List[str] # Returns validation errors
def to_dict(self) -> Dict[str, Any]
```
### TestSuite
Test suite configuration containing multiple test cases.
```python
from mcptesta.yaml_parser.parser import TestSuite
class TestSuite:
def __init__(
self,
name: str,
tests: List[TestCase],
description: Optional[str] = None,
parallel: bool = True,
timeout: Optional[int] = None,
tags: Optional[List[str]] = None,
setup: Optional[Dict[str, Any]] = None,
teardown: Optional[Dict[str, Any]] = None,
enabled: bool = True
) -> None
def get_execution_layers(self) -> List[List[TestCase]]
def validate(self) -> List[str]
```
## Test Runners
### ParallelTestRunner
Main test runner for parallel execution.
```python
from mcptesta.runners.parallel import ParallelTestRunner
from mcptesta.core.session import TestSession
class ParallelTestRunner:
def __init__(
self,
config: TestConfig,
reporters: List[Any] = None
) -> None
async def run(self, session: TestSession) -> ExecutionResults
async def run_test_suite(self, suite: TestSuite, session: TestSession) -> SuiteResults
async def run_test_case(self, test: TestCase, client: MCPTestClient) -> TestResult
```
#### Usage Example
```python
from mcptesta.runners.parallel import ParallelTestRunner
from mcptesta.core.config import TestConfig
from mcptesta.core.session import TestSession
async def run_tests():
config = TestConfig(
parallel_workers=4,
output_format="html",
output_directory="./results"
)
session = TestSession(config)
runner = ParallelTestRunner(config)
results = await runner.run(session)
print(f"Tests completed: {results.total_tests}")
print(f"Passed: {results.passed}")
print(f"Failed: {results.failed}")
```
### SequentialTestRunner
Sequential test runner for ordered execution.
```python
from mcptesta.runners.sequential import SequentialTestRunner
class SequentialTestRunner:
def __init__(
self,
config: TestConfig,
reporters: List[Any] = None
) -> None
async def run(self, session: TestSession) -> ExecutionResults
```
## Protocol Features
### ProtocolFeatures
Advanced MCP protocol feature testing.
```python
from mcptesta.protocol.features import ProtocolFeatures
class ProtocolFeatures:
def __init__(self) -> None
async def test_notifications(self, client: MCPTestClient) -> bool
async def test_progress(self, client: MCPTestClient) -> bool
async def test_cancellation(self, client: MCPTestClient) -> bool
async def test_sampling(self, client: MCPTestClient) -> bool
async def subscribe_notifications(
self,
client: MCPTestClient,
notification_types: List[str]
) -> AsyncIterator[Dict[str, Any]]
async def monitor_progress(
self,
client: MCPTestClient,
operation_id: str
) -> AsyncIterator[Dict[str, Any]]
```
#### Usage Example
```python
from mcptesta.protocol.features import ProtocolFeatures
async def test_advanced_features():
features = ProtocolFeatures()
async with MCPTestClient(config) as client:
# Test notification support
supports_notifications = await features.test_notifications(client)
if supports_notifications:
# Subscribe to notifications
async for notification in features.subscribe_notifications(
client, ["resources_list_changed"]
):
print(f"Received notification: {notification}")
```
## YAML Parser
### YAMLTestParser
Parser for YAML configuration files.
```python
from mcptesta.yaml_parser.parser import YAMLTestParser
class YAMLTestParser:
def __init__(self) -> None
def parse_file(self, config_path: str) -> TestConfig
def parse_string(self, config_content: str) -> TestConfig
def validate_schema(self, config_dict: Dict[str, Any]) -> List[str]
def substitute_variables(
self,
content: str,
variables: Dict[str, str]
) -> str
```
#### Usage Example
```python
from mcptesta.yaml_parser.parser import YAMLTestParser
parser = YAMLTestParser()
# Parse from file
config = parser.parse_file("tests.yaml")
# Parse from string
yaml_content = """
config:
parallel_workers: 2
servers:
- name: "test_server"
command: "python server.py"
"""
config = parser.parse_string(yaml_content)
```
## Reporters
### ConsoleReporter
Rich console output reporter.
```python
from mcptesta.reporters.console import ConsoleReporter
class ConsoleReporter:
def __init__(self, use_rich: bool = True) -> None
async def start_test_suite(self, suite: TestSuite) -> None
async def end_test_suite(self, suite: TestSuite, results: SuiteResults) -> None
async def report_test_result(self, test: TestCase, result: TestResult) -> None
async def report_final_summary(self, results: ExecutionResults) -> None
```
### HTMLReporter
HTML report generator.
```python
from mcptesta.reporters.html import HTMLReporter
class HTMLReporter:
def __init__(self, output_directory: str) -> None
async def generate_report(self, results: ExecutionResults) -> str
def create_test_timeline(self, results: ExecutionResults) -> str
def create_performance_charts(self, results: ExecutionResults) -> str
```
## Utilities
### Validation
Validation utilities for server connections and configurations.
```python
from mcptesta.utils.validation import validate_server_connection, validate_yaml_config
async def validate_server_connection(config: ServerConfig) -> Dict[str, Any]:
"""Validate server connection and return capabilities."""
def validate_yaml_config(config_path: str) -> List[str]:
"""Validate YAML configuration and return errors."""
```
### Metrics
Performance metrics collection.
```python
from mcptesta.utils.metrics import MetricsCollector
class MetricsCollector:
def __init__(self) -> None
def start_timer(self, operation: str) -> str
def end_timer(self, timer_id: str) -> float
def record_memory_usage(self, operation: str) -> None
def get_performance_summary(self) -> Dict[str, Any]
```
### Logging
Logging configuration and utilities.
```python
from mcptesta.utils.logging import setup_logging, LoggingConfig
class LoggingConfig:
def __init__(
self,
level: str = "INFO",
console_output: bool = True,
file_output: Optional[str] = None,
use_rich_console: bool = True,
rich_tracebacks: bool = True
) -> None
def setup_logging(config: LoggingConfig) -> None:
"""Configure logging based on LoggingConfig."""
```
## Custom Extensions
### Creating Custom Test Types
```python
from mcptesta.core.client import MCPTestClient, TestResult
class CustomTestRunner:
async def run_custom_test(
self,
client: MCPTestClient,
test_config: Dict[str, Any]
) -> TestResult:
"""Implement custom test logic."""
start_time = time.time()
try:
# Custom test implementation
result = await self.execute_custom_logic(client, test_config)
return TestResult(
test_name=test_config["name"],
success=True,
execution_time=time.time() - start_time,
response_data=result
)
except Exception as e:
return TestResult(
test_name=test_config["name"],
success=False,
execution_time=time.time() - start_time,
error_message=str(e)
)
async def execute_custom_logic(
self,
client: MCPTestClient,
config: Dict[str, Any]
) -> Any:
"""Custom test logic implementation."""
pass
```
### Custom Reporters
```python
from mcptesta.reporters.base import BaseReporter
class CustomReporter(BaseReporter):
async def start_test_suite(self, suite: TestSuite) -> None:
"""Called when test suite starts."""
pass
async def end_test_suite(self, suite: TestSuite, results: SuiteResults) -> None:
"""Called when test suite ends."""
pass
async def report_test_result(self, test: TestCase, result: TestResult) -> None:
"""Called for each test result."""
pass
async def report_final_summary(self, results: ExecutionResults) -> None:
"""Called at the end of all tests."""
pass
```
## Error Handling
### Exception Classes
```python
from mcptesta.core.exceptions import (
MCPTestError,
ConnectionError,
TimeoutError,
AuthenticationError,
ConfigurationError,
ValidationError
)
class MCPTestError(Exception):
"""Base exception for MCPTesta."""
class ConnectionError(MCPTestError):
"""Server connection failed."""
class TimeoutError(MCPTestError):
"""Operation timed out."""
class AuthenticationError(MCPTestError):
"""Authentication failed."""
class ConfigurationError(MCPTestError):
"""Configuration error."""
class ValidationError(MCPTestError):
"""Validation error."""
```
### Error Handling Example
```python
from mcptesta.core.exceptions import ConnectionError, TimeoutError
async def robust_testing():
try:
async with MCPTestClient(config) as client:
result = await client.call_tool("my_tool")
except ConnectionError as e:
print(f"Connection failed: {e}")
except TimeoutError as e:
print(f"Operation timed out: {e}")
except Exception as e:
print(f"Unexpected error: {e}")
```
This API reference provides complete documentation for programmatic usage of MCPTesta. For practical examples and usage patterns, see the [Tutorials](/tutorials/) and [How-to Guides](/how-to/) sections.

View File

@ -0,0 +1,891 @@
---
title: CLI Reference
description: Complete command-line interface reference for MCPTesta - comprehensive FastMCP testing
---
Complete command-line interface reference for MCPTesta's enterprise-grade FastMCP testing capabilities. This reference covers all commands, options, advanced configuration, and enterprise deployment patterns.
## Main Command
### mcptesta
```bash
mcptesta [GLOBAL_OPTIONS] COMMAND [COMMAND_OPTIONS] [ARGS]...
```
**Description:** Main entry point for MCPTesta - Comprehensive FastMCP Test Client with enterprise-grade capabilities for production testing, performance analysis, and compliance validation.
**Global Options:**
- `--version` - Show detailed version information including dependency versions
- `--verbose, -v` - Increase verbosity level (use multiple times: -v, -vv, -vvv)
- `--quiet, -q` - Suppress all output except errors
- `--config PATH` - Specify custom configuration file path
- `--profile` - Enable execution profiling for performance analysis
- `--help` - Show comprehensive help message with examples
**Verbosity Levels:**
- Default: Errors and warnings only
- `-v`: Add informational messages
- `-vv`: Add debug messages and detailed execution logs
- `-vvv`: Add trace-level debugging with full protocol messages
## Core Commands
### test
```bash
mcptesta test [OPTIONS]
```
**Description:** Execute comprehensive FastMCP server testing with CLI parameters. Supports advanced protocol features, performance testing, and enterprise authentication.
#### Required Options
- `--server, -s TEXT` - Server command or connection string **[required]**
- Examples: `"python my_server.py"`, `"uvx my-mcp-server"`, `"https://api.company.com/mcp"`
#### Connection and Transport Options
- `--transport, -t [stdio|sse|ws]` - Transport protocol [default: stdio]
- `stdio`: Process-based communication (default)
- `sse`: Server-Sent Events over HTTP/HTTPS
- `ws`: WebSocket connections (including WSS)
- `--timeout INTEGER` - Connection timeout in seconds [default: 30]
- `--retry-attempts INTEGER` - Number of connection retry attempts [default: 3]
- `--retry-backoff FLOAT` - Backoff factor for retries [default: 2.0]
- `--keep-alive INTEGER` - Keep-alive interval for persistent connections [default: 30]
#### Authentication Options
- `--auth-token TEXT` - Bearer token for authentication
- `--oauth-client-id TEXT` - OAuth 2.0 client ID
- `--oauth-client-secret TEXT` - OAuth 2.0 client secret
- `--oauth-token-url TEXT` - OAuth 2.0 token endpoint URL
- `--oauth-scope TEXT` - OAuth 2.0 scope (can be used multiple times)
- `--basic-auth TEXT` - Basic authentication in format "username:password"
- `--api-key TEXT` - API key for custom authentication
- `--auth-header TEXT` - Custom authentication header in format "Header: Value"
#### Execution Control Options
- `--parallel, -p INTEGER` - Number of parallel test workers [default: 1]
- `--max-concurrent INTEGER` - Maximum concurrent operations [default: 10]
- `--worker-timeout INTEGER` - Maximum time per worker in seconds [default: 300]
- `--global-timeout INTEGER` - Global test execution timeout [default: 1800]
- `--rate-limit FLOAT` - Requests per second rate limit [default: no limit]
- `--burst-size INTEGER` - Maximum burst size for rate limiting [default: 10]
- `--graceful-shutdown INTEGER` - Graceful shutdown timeout in seconds [default: 30]
#### Output and Reporting Options
- `--output, -o PATH` - Output directory for reports [default: ./mcptesta_results]
- `--format [console|html|json|junit|xml|csv]` - Output format [default: console]
- `console`: Rich console output with colors and progress bars
- `html`: Interactive HTML report with charts and filtering
- `json`: Structured JSON for programmatic processing
- `junit`: JUnit XML for CI/CD integration
- `xml`: Generic XML format
- `csv`: CSV format for data analysis
- `--report-title TEXT` - Custom title for generated reports
- `--include-logs` - Include detailed logs in reports
- `--compress-output` - Compress output files for storage efficiency
- `--upload-results TEXT` - Upload results to specified endpoint
#### Test Selection and Filtering
- `--include-tools TEXT` - Comma-separated list of tools to test
- `--exclude-tools TEXT` - Comma-separated list of tools to exclude
- `--include-resources TEXT` - Comma-separated list of resources to test
- `--exclude-resources TEXT` - Comma-separated list of resources to exclude
- `--include-prompts TEXT` - Comma-separated list of prompts to test
- `--exclude-prompts TEXT` - Comma-separated list of prompts to exclude
- `--test-pattern TEXT` - Regular expression pattern for test selection
- `--tag TEXT` - Run only tests with specified tags (can be used multiple times)
- `--exclude-tag TEXT` - Exclude tests with specified tags
#### Advanced Protocol Features
- `--test-notifications` - Test MCP notification features (list changes, custom notifications)
- `--test-cancellation` - Test request cancellation and cleanup mechanisms
- `--test-progress` - Test progress reporting for long-running operations
- `--test-sampling` - Test request sampling and throttling mechanisms
- `--test-auth` - Test authentication and authorization boundaries
- `--test-session-management` - Test session creation, management, and cleanup
- `--test-error-handling` - Test comprehensive error scenario handling
- `--test-resource-management` - Test resource lifecycle and cleanup
#### Performance and Load Testing
- `--stress-test` - Enable comprehensive stress testing mode
- `--memory-profile` - Enable detailed memory profiling and leak detection
- `--performance-profile` - Enable comprehensive performance profiling
- `--cpu-profile` - Enable CPU utilization profiling
- `--network-profile` - Enable network usage profiling
- `--load-duration INTEGER` - Duration for load testing in seconds [default: 60]
- `--concurrent-users INTEGER` - Number of concurrent simulated users [default: 10]
- `--ramp-up-time INTEGER` - Ramp-up time for load testing in seconds [default: 30]
- `--think-time FLOAT` - Delay between operations in seconds [default: 1.0]
#### Enterprise and Compliance Options
- `--compliance-mode` - Enable compliance logging and audit trails
- `--audit-trail PATH` - Path for audit trail log file
- `--evidence-collection` - Enable comprehensive evidence collection
- `--soc2-compliance` - Enable SOC 2 compliance features
- `--hipaa-compliance` - Enable HIPAA compliance features
- `--gdpr-compliance` - Enable GDPR compliance features
- `--data-classification TEXT` - Classify data for compliance purposes
#### Examples
```bash
# Basic server testing
mcptesta test --server "python my_server.py"
# Enterprise production testing with authentication
mcptesta test \
--server "https://api.production.company.com/mcp" \
--transport sse \
--auth-token "${PROD_TOKEN}" \
--parallel 4 \
--test-notifications \
--test-cancellation \
--test-progress \
--test-auth \
--format html \
--output ./production_results \
--compliance-mode \
--audit-trail ./audit.log
# Advanced performance testing with profiling
mcptesta test \
--server "uvx high-performance-server" \
--parallel 16 \
--stress-test \
--memory-profile \
--performance-profile \
--cpu-profile \
--load-duration 300 \
--concurrent-users 100 \
--ramp-up-time 60 \
--format html \
--report-title "Performance Validation"
# OAuth-protected server testing
mcptesta test \
--server "https://oauth-api.company.com/mcp" \
--transport sse \
--oauth-client-id "${CLIENT_ID}" \
--oauth-client-secret "${CLIENT_SECRET}" \
--oauth-token-url "https://auth.company.com/token" \
--oauth-scope "mcp:read" \
--oauth-scope "mcp:write" \
--test-auth \
--format json
# Multi-format output generation
mcptesta test \
--server "python comprehensive_server.py" \
--parallel 8 \
--format html \
--format json \
--format junit \
--output ./comprehensive_results \
--include-logs \
--compress-output
```
### yaml
```bash
mcptesta yaml [OPTIONS] CONFIG_PATH
```
**Description:** Execute comprehensive testing using YAML configuration files with advanced override capabilities and enterprise features.
#### Arguments
- `CONFIG_PATH` - Path to YAML configuration file **[required]**
- Supports local files, URLs, and configuration templates
- Can be a directory containing multiple YAML files
#### Configuration Override Options
- `--parallel, -p INTEGER` - Override parallel workers from configuration
- `--output, -o PATH` - Override output directory from configuration
- `--format [console|html|json|junit|xml|csv]` - Override output format
- `--timeout INTEGER` - Override global timeout from configuration
- `--server-override TEXT` - Override server configuration
- `--auth-token-override TEXT` - Override authentication token
- `--variables TEXT` - Override configuration variables in format "key=value"
- `--environment TEXT` - Set environment for environment-specific configurations
#### Execution Control Options
- `--dry-run` - Validate configuration without executing tests
- `--validate-only` - Validate configuration and server connectivity only
- `--list-tests` - List all tests that would be executed
- `--list-suites` - List all test suites in configuration
- `--explain-dependencies` - Show dependency resolution and execution plan
- `--continue-on-failure` - Continue execution even if tests fail
- `--fail-fast` - Stop execution on first test failure
- `--max-failures INTEGER` - Maximum number of failures before stopping
#### Test Selection and Filtering Options
- `--filter TEXT` - Filter tests by name pattern (supports regex)
- `--suite TEXT` - Run only specified test suites (can be used multiple times)
- `--exclude-suite TEXT` - Exclude specified test suites
- `--tag TEXT` - Run only tests with specified tags (can be used multiple times)
- `--exclude-tag TEXT` - Exclude tests with specified tags
- `--priority [critical|high|medium|low]` - Run only tests with specified priority
- `--test-type [ping|tool_call|resource_read|prompt_get|notification]` - Filter by test type
#### Advanced Execution Options
- `--parallel-suites` - Enable parallel execution across test suites
- `--worker-distribution [round_robin|least_loaded|weighted]` - Worker distribution strategy
- `--resource-limits` - Enable resource limit enforcement
- `--memory-limit TEXT` - Maximum memory per worker (e.g., "512MB", "2GB")
- `--cpu-limit FLOAT` - Maximum CPU usage per worker (0.0-1.0)
- `--network-limit TEXT` - Network bandwidth limit per worker
#### Monitoring and Observability
- `--enable-metrics` - Enable comprehensive metrics collection
- `--metrics-endpoint TEXT` - Send metrics to specified endpoint
- `--enable-tracing` - Enable distributed tracing
- `--trace-endpoint TEXT` - Send traces to specified endpoint
- `--health-check-interval INTEGER` - Health check interval in seconds
- `--dashboard-port INTEGER` - Enable real-time dashboard on specified port
#### Examples
```bash
# Basic YAML execution
mcptesta yaml tests.yaml
# Production execution with overrides
mcptesta yaml production_tests.yaml \
--parallel 16 \
--output ./production_results \
--format html \
--format json \
--variables "ENVIRONMENT=production" \
--variables "AUTH_TOKEN=${PROD_TOKEN}" \
--continue-on-failure
# Configuration validation and planning
mcptesta yaml complex_tests.yaml \
--dry-run \
--explain-dependencies \
--list-tests
# Filtered execution with monitoring
mcptesta yaml comprehensive_tests.yaml \
--tag "critical" \
--tag "performance" \
--exclude-tag "slow" \
--enable-metrics \
--dashboard-port 8080 \
--parallel-suites
# Enterprise compliance execution
mcptesta yaml compliance_tests.yaml \
--variables "COMPLIANCE_FRAMEWORK=SOC2" \
--variables "AUDIT_PERIOD=Q4_2024" \
--resource-limits \
--memory-limit "1GB" \
--cpu-limit 0.8 \
--continue-on-failure \
--format junit \
--format html
# Multi-environment execution
mcptesta yaml multi_env_tests.yaml \
--environment "staging" \
--server-override "https://staging-api.company.com/mcp" \
--auth-token-override "${STAGING_TOKEN}" \
--parallel 4
```
### validate
```bash
mcptesta validate [OPTIONS]
```
**Description:** Comprehensive server validation including connection testing, capability discovery, authentication verification, and compatibility analysis.
#### Required Options
- `--server, -s TEXT` - Server command or connection string **[required]**
#### Connection Options
- `--transport, -t [stdio|sse|ws]` - Transport protocol [default: stdio]
- `--timeout INTEGER` - Connection timeout in seconds [default: 10]
- `--retry-attempts INTEGER` - Number of retry attempts [default: 3]
- `--connection-pool-size INTEGER` - Connection pool size for testing [default: 5]
#### Authentication Options
- `--auth-token TEXT` - Bearer token for authentication testing
- `--oauth-client-id TEXT` - OAuth client ID for authentication testing
- `--oauth-client-secret TEXT` - OAuth client secret
- `--oauth-token-url TEXT` - OAuth token endpoint
- `--test-auth-boundaries` - Test authentication and authorization boundaries
#### Validation Scope Options
- `--basic-only` - Perform only basic connectivity validation
- `--comprehensive` - Perform comprehensive validation including all features
- `--capability-discovery` - Discover and validate all server capabilities
- `--protocol-compliance` - Test MCP protocol compliance
- `--performance-validation` - Include basic performance validation
- `--security-validation` - Include security and authentication validation
- `--compatibility-check` - Check compatibility with MCPTesta features
#### Output Options
- `--format [console|json|yaml]` - Output format [default: console]
- `--output PATH` - Save validation report to file
- `--detailed` - Include detailed validation information
- `--show-capabilities` - Display discovered server capabilities
- `--show-errors-only` - Show only validation errors
#### Examples
```bash
# Basic server validation
mcptesta validate --server "python my_server.py"
# Comprehensive production server validation
mcptesta validate \
--server "https://api.production.company.com/mcp" \
--transport sse \
--auth-token "${PROD_TOKEN}" \
--comprehensive \
--security-validation \
--performance-validation \
--format json \
--output ./validation_report.json
# OAuth server validation
mcptesta validate \
--server "https://oauth-api.company.com/mcp" \
--transport sse \
--oauth-client-id "${CLIENT_ID}" \
--oauth-client-secret "${CLIENT_SECRET}" \
--oauth-token-url "https://auth.company.com/token" \
--test-auth-boundaries \
--detailed
# Quick capability discovery
mcptesta validate \
--server "uvx my-mcp-server" \
--capability-discovery \
--show-capabilities \
--format yaml
```
### ping
```bash
mcptesta ping [OPTIONS]
```
**Description:** Advanced connectivity testing with latency analysis, throughput measurement, and connection stability assessment.
#### Required Options
- `--server, -s TEXT` - Server command or connection string **[required]**
#### Connection Options
- `--transport, -t [stdio|sse|ws]` - Transport protocol [default: stdio]
- `--timeout INTEGER` - Individual ping timeout in seconds [default: 5]
#### Ping Configuration Options
- `--count INTEGER` - Number of ping requests [default: 10]
- `--interval FLOAT` - Interval between pings in seconds [default: 1.0]
- `--payload-size INTEGER` - Ping payload size in bytes [default: 64]
- `--concurrent INTEGER` - Number of concurrent ping streams [default: 1]
- `--duration INTEGER` - Test duration in seconds (overrides count)
#### Advanced Testing Options
- `--flood` - Send pings as fast as possible (flood mode)
- `--adaptive` - Use adaptive ping intervals based on response times
- `--packet-loss-detection` - Enable packet loss detection and analysis
- `--jitter-analysis` - Perform network jitter analysis
- `--throughput-test` - Include throughput testing with ping
- `--connection-stability` - Test connection stability over time
#### Output and Analysis Options
- `--format [console|json|csv]` - Output format [default: console]
- `--output PATH` - Save ping results to file
- `--statistics` - Show detailed statistics and analysis
- `--histogram` - Show response time histogram
- `--percentiles` - Show response time percentiles
- `--real-time` - Show real-time ping results
#### Examples
```bash
# Basic connectivity ping
mcptesta ping --server "python my_server.py"
# Comprehensive network analysis
mcptesta ping \
--server "https://api.company.com/mcp" \
--transport sse \
--count 100 \
--interval 0.5 \
--concurrent 4 \
--jitter-analysis \
--throughput-test \
--statistics \
--histogram \
--format json \
--output ./ping_analysis.json
# Production server monitoring
mcptesta ping \
--server "wss://secure-api.company.com/mcp" \
--transport ws \
--duration 300 \
--adaptive \
--connection-stability \
--real-time
# Stress testing with flood mode
mcptesta ping \
--server "local_server.py" \
--flood \
--duration 60 \
--packet-loss-detection \
--percentiles
```
### generate-config
```bash
mcptesta generate-config [OPTIONS] TEMPLATE OUTPUT_PATH
```
**Description:** Generate comprehensive YAML configuration templates with enterprise patterns, compliance frameworks, and advanced testing scenarios.
#### Arguments
- `TEMPLATE` - Template complexity level **[required]**
- `basic`: Simple connectivity and tool testing
- `intermediate`: Multi-suite testing with dependencies
- `advanced`: Full protocol features and enterprise patterns
- `expert`: Complex scenarios and performance testing
- `stress`: Specialized performance and load testing
- `integration`: Multi-service and CI/CD integration
- `compliance`: Compliance-focused testing (SOC2, HIPAA, etc.)
- `production`: Production-ready enterprise template
- `OUTPUT_PATH` - Output file path **[required]**
#### Server Configuration Options
- `--server-command TEXT` - Custom server command for template
- `--transport [stdio|sse|ws]` - Default transport protocol
- `--server-count INTEGER` - Number of server instances in template
- `--multi-environment` - Generate multi-environment configuration
#### Test Configuration Options
- `--test-types TEXT` - Comma-separated test types (tool_call,resource_read,prompt_get,notification,ping)
- `--parallel-workers INTEGER` - Number of parallel workers [default: 4]
- `--enable-features TEXT` - Comma-separated features (notifications,progress,cancellation,sampling,auth)
- `--test-complexity [simple|standard|comprehensive]` - Test complexity level
- `--include-performance-tests` - Include performance testing scenarios
- `--include-security-tests` - Include security and authentication tests
#### Enterprise and Compliance Options
- `--compliance-framework [SOC2|HIPAA|GDPR|PCI|ISO27001]` - Compliance framework
- `--enterprise-features` - Include enterprise-specific features
- `--monitoring-integration` - Include monitoring platform integration
- `--ci-cd-integration` - Include CI/CD pipeline integration
- `--incident-management` - Include incident management integration
- `--audit-requirements` - Include audit trail and evidence collection
#### Customization Options
- `--organization TEXT` - Organization name for configuration
- `--environment TEXT` - Target environment (development,staging,production)
- `--custom-variables TEXT` - Custom variables in format "key=value"
- `--include-examples` - Include comprehensive usage examples
- `--include-documentation` - Include inline documentation
#### Template Descriptions
**basic**
- Minimal configuration for getting started
- Single server, basic tool testing
- Console output, sequential execution
- Perfect for learning and simple scenarios
**intermediate**
- Multi-suite organization with test dependencies
- Basic parallel execution and error handling
- HTML reporting and multiple test types
- Suitable for regular development testing
**advanced**
- Comprehensive MCP protocol feature testing
- Enterprise authentication and authorization
- Advanced parallel execution with optimization
- Performance monitoring and profiling
- Multiple output formats and detailed reporting
**expert**
- Maximum complexity with sophisticated patterns
- Complex dependency chains and execution flows
- Multi-server load balancing and distribution
- Advanced performance analysis and optimization
- Enterprise resource management and scaling
**stress**
- Specialized performance and load testing
- High concurrency and throughput testing
- Memory, CPU, and network profiling
- Stress scenarios and limit testing
- Performance regression analysis
**integration**
- Multi-service integration testing patterns
- External dependency and API testing
- Environment-specific configurations
- CI/CD pipeline integration patterns
- Cross-system compatibility testing
**compliance**
- SOC 2, HIPAA, GDPR compliance frameworks
- Audit trail and evidence collection
- Access control and security validation
- Data classification and handling
- Regulatory reporting requirements
**production**
- Production-ready enterprise configuration
- Comprehensive monitoring and alerting
- Incident management integration
- Security and compliance features
- Operational excellence patterns
#### Examples
```bash
# Generate basic template for beginners
mcptesta generate-config basic ./my_first_tests.yaml \
--server-command "python my_server.py" \
--include-examples
# Generate enterprise production template
mcptesta generate-config production ./production_tests.yaml \
--server-command "https://api.production.company.com/mcp" \
--transport sse \
--parallel-workers 16 \
--enterprise-features \
--monitoring-integration \
--incident-management \
--organization "Acme Corporation" \
--environment "production"
# Generate compliance-focused template
mcptesta generate-config compliance ./sox_compliance_tests.yaml \
--compliance-framework SOC2 \
--audit-requirements \
--enterprise-features \
--include-documentation \
--organization "Financial Services Corp"
# Generate advanced testing template
mcptesta generate-config advanced ./advanced_tests.yaml \
--server-command "uvx advanced-mcp-server" \
--enable-features "notifications,progress,cancellation,sampling,auth" \
--include-performance-tests \
--include-security-tests \
--parallel-workers 8 \
--test-complexity comprehensive
# Generate stress testing template
mcptesta generate-config stress ./stress_tests.yaml \
--server-command "python high_performance_server.py" \
--parallel-workers 32 \
--multi-environment \
--include-examples
# Generate CI/CD integration template
mcptesta generate-config integration ./ci_cd_tests.yaml \
--ci-cd-integration \
--test-types "tool_call,resource_read,prompt_get" \
--custom-variables "CI_ENVIRONMENT=github_actions" \
--custom-variables "NOTIFICATION_WEBHOOK=${CI_WEBHOOK}"
```
## Utility Commands
### config
```bash
mcptesta config [OPTIONS] [SUBCOMMAND]
```
**Description:** Manage MCPTesta configuration files and settings.
#### Subcommands
- `show` - Display current configuration
- `init` - Initialize configuration in current directory
- `validate` - Validate configuration file syntax
- `migrate` - Migrate configuration to latest format
- `reset` - Reset to default configuration
#### Examples
```bash
# Show current configuration
mcptesta config show
# Initialize new configuration
mcptesta config init --template advanced
# Validate configuration file
mcptesta config validate ./my_config.yaml
```
### completion
```bash
mcptesta completion [SHELL]
```
**Description:** Generate shell completion scripts for enhanced command-line experience.
#### Arguments
- `SHELL` - Target shell: `bash`, `zsh`, `fish`, `powershell`
#### Examples
```bash
# Generate bash completion
mcptesta completion bash > ~/.bash_completion.d/mcptesta
# Generate zsh completion
mcptesta completion zsh > ~/.zsh/completions/_mcptesta
# Generate fish completion
mcptesta completion fish > ~/.config/fish/completions/mcptesta.fish
```
### doctor
```bash
mcptesta doctor [OPTIONS]
```
**Description:** Diagnose MCPTesta installation and environment for troubleshooting.
#### Options
- `--verbose` - Show detailed diagnostic information
- `--fix` - Attempt to fix common issues automatically
- `--export PATH` - Export diagnostic report to file
#### Examples
```bash
# Basic health check
mcptesta doctor
# Detailed diagnostics with fixes
mcptesta doctor --verbose --fix
# Export diagnostic report
mcptesta doctor --export ./diagnostics.json
```
## Global Configuration
### Exit Codes
MCPTesta uses comprehensive exit codes for precise error handling:
- `0` - **Success**: All tests passed successfully
- `1` - **Test Failures**: One or more tests failed
- `2` - **Configuration Error**: Invalid configuration or parameters
- `3` - **Connection Error**: Unable to connect to server
- `4` - **Authentication Error**: Authentication or authorization failed
- `5` - **Protocol Error**: MCP protocol violation or incompatibility
- `6` - **Timeout Error**: Operation timed out
- `7` - **Resource Error**: Insufficient system resources
- `8` - **Permission Error**: Insufficient permissions
- `9` - **Validation Error**: Input validation failed
- `130` - **Interrupted**: User cancelled with Ctrl+C
- `255` - **Unknown Error**: Unexpected error occurred
### Environment Variables
MCPTesta recognizes comprehensive environment variables for configuration:
#### Authentication and Security
- `MCPTESTA_AUTH_TOKEN` - Default authentication token
- `MCPTESTA_OAUTH_CLIENT_ID` - OAuth 2.0 client ID
- `MCPTESTA_OAUTH_CLIENT_SECRET` - OAuth 2.0 client secret
- `MCPTESTA_OAUTH_TOKEN_URL` - OAuth 2.0 token endpoint URL
- `MCPTESTA_API_KEY` - Default API key for authentication
- `MCPTESTA_BASIC_AUTH` - Basic authentication credentials
- `MCPTESTA_TLS_CERT_PATH` - Path to TLS client certificate
- `MCPTESTA_TLS_KEY_PATH` - Path to TLS client private key
- `MCPTESTA_TLS_CA_PATH` - Path to TLS certificate authority
#### Server Configuration
- `MCPTESTA_SERVER_COMMAND` - Default server command
- `MCPTESTA_SERVER_TIMEOUT` - Default server timeout in seconds
- `MCPTESTA_SERVER_TRANSPORT` - Default transport protocol
- `MCPTESTA_SERVER_RETRY_ATTEMPTS` - Default retry attempts
- `MCPTESTA_SERVER_RETRY_BACKOFF` - Default retry backoff factor
#### Execution Configuration
- `MCPTESTA_PARALLEL_WORKERS` - Default number of parallel workers
- `MCPTESTA_MAX_CONCURRENT` - Default maximum concurrent operations
- `MCPTESTA_GLOBAL_TIMEOUT` - Default global timeout in seconds
- `MCPTESTA_RATE_LIMIT` - Default rate limit (requests per second)
- `MCPTESTA_MEMORY_LIMIT` - Default memory limit per worker
#### Output and Reporting
- `MCPTESTA_OUTPUT_DIR` - Default output directory
- `MCPTESTA_OUTPUT_FORMAT` - Default output format
- `MCPTESTA_LOG_LEVEL` - Logging level (TRACE, DEBUG, INFO, WARNING, ERROR)
- `MCPTESTA_LOG_FORMAT` - Log format (console, json, structured)
- `MCPTESTA_REPORT_TITLE` - Default report title
- `MCPTESTA_COMPRESS_OUTPUT` - Enable output compression (true/false)
#### Enterprise and Compliance
- `MCPTESTA_COMPLIANCE_MODE` - Enable compliance mode (true/false)
- `MCPTESTA_AUDIT_TRAIL_PATH` - Default audit trail log path
- `MCPTESTA_EVIDENCE_COLLECTION` - Enable evidence collection (true/false)
- `MCPTESTA_DATA_CLASSIFICATION` - Default data classification level
- `MCPTESTA_ORGANIZATION` - Organization name for reporting
#### Monitoring and Integration
- `MCPTESTA_METRICS_ENDPOINT` - Metrics collection endpoint
- `MCPTESTA_TRACE_ENDPOINT` - Distributed tracing endpoint
- `MCPTESTA_WEBHOOK_URL` - Notification webhook URL
- `MCPTESTA_SLACK_WEBHOOK` - Slack notification webhook
- `MCPTESTA_TEAMS_WEBHOOK` - Microsoft Teams notification webhook
#### Development and Debugging
- `MCPTESTA_DEBUG` - Enable debug mode (true/false)
- `MCPTESTA_PROFILE` - Enable profiling (true/false)
- `MCPTESTA_TRACE` - Enable tracing (true/false)
- `MCPTESTA_DEV_MODE` - Enable development mode (true/false)
### Configuration File Locations
MCPTesta searches for configuration files in the following order:
1. **Command-line specified**: `--config /path/to/config.yaml`
2. **Current directory**: `./mcptesta.yaml` or `./mcptesta.yml`
3. **Project root**: `./.mcptesta/config.yaml`
4. **User config directory**:
- Linux/macOS: `~/.config/mcptesta/config.yaml`
- Windows: `%APPDATA%\mcptesta\config.yaml`
5. **System config directory**:
- Linux: `/etc/mcptesta/config.yaml`
- macOS: `/usr/local/etc/mcptesta/config.yaml`
- Windows: `%PROGRAMDATA%\mcptesta\config.yaml`
### Logging Configuration
MCPTesta provides comprehensive logging with multiple output formats:
#### Log Levels
- `TRACE`: Detailed protocol-level debugging
- `DEBUG`: Development and troubleshooting information
- `INFO`: General operational information
- `WARNING`: Warning conditions and potential issues
- `ERROR`: Error conditions requiring attention
#### Log Formats
- `console`: Human-readable colored output with Rich formatting
- `json`: Structured JSON for log aggregation systems
- `structured`: Key-value structured format
#### Log Destinations
- **Console**: Standard output with color and formatting
- **File**: Rotating log files with configurable retention
- **Syslog**: System logging for enterprise environments
- **Remote**: HTTP/HTTPS endpoints for centralized logging
### Performance Optimization
#### Parallel Worker Guidelines
**CPU-Bound Workloads:**
- Default: Number of CPU cores
- Recommended: 1x CPU cores for compute-intensive operations
- Maximum: Monitor CPU utilization to avoid oversubscription
**I/O-Bound Workloads:**
- Default: 2x CPU cores
- Recommended: 2-4x CPU cores for network/disk operations
- Maximum: Monitor connection limits and memory usage
**Mixed Workloads:**
- Default: 1.5x CPU cores
- Recommended: Profile actual workload characteristics
- Maximum: Adjust based on resource monitoring
#### Memory Management
- Each worker consumes base memory plus connection overhead
- HTML report generation requires additional memory
- Performance profiling significantly increases memory usage
- Monitor memory usage with `--memory-profile`
#### Network Considerations
- SSE and WebSocket maintain persistent connections
- Multiple workers create multiple server connections
- Consider server connection limits and network bandwidth
- Use rate limiting for production server testing
### Security Best Practices
#### Credential Management
- **Never hardcode credentials** in configuration files or command lines
- **Use environment variables** for sensitive authentication data
- **Implement credential rotation** for production environments
- **Use secure storage systems** (HashiCorp Vault, AWS Secrets Manager, etc.)
- **Apply principle of least privilege** for authentication tokens
#### Network Security
- **Use TLS/SSL** for all production communications (HTTPS, WSS)
- **Validate server certificates** in production environments
- **Implement network segmentation** for test environments
- **Use VPN or secure channels** for remote server testing
- **Monitor and log** all authentication attempts
#### Compliance Requirements
- **Enable audit trails** for compliance frameworks
- **Implement data classification** for sensitive information
- **Maintain evidence collection** for regulatory requirements
- **Follow organizational security policies** and procedures
- **Regular security assessments** of testing infrastructure
### Shell Integration
#### Completion Installation
**Bash:**
```bash
# Add to ~/.bashrc
eval "$(_MCPTESTA_COMPLETE=bash_source mcptesta)"
# Or install globally
mcptesta completion bash | sudo tee /etc/bash_completion.d/mcptesta
```
**Zsh:**
```bash
# Add to ~/.zshrc
eval "$(_MCPTESTA_COMPLETE=zsh_source mcptesta)"
# Or install to completion directory
mcptesta completion zsh > ~/.zsh/completions/_mcptesta
```
**Fish:**
```bash
# Install to fish completions
mcptesta completion fish > ~/.config/fish/completions/mcptesta.fish
```
**PowerShell:**
```powershell
# Add to PowerShell profile
mcptesta completion powershell >> $PROFILE
```
#### Advanced Shell Features
- **Smart completion** for server commands and configuration files
- **Context-aware suggestions** for command options
- **Dynamic completion** for test names and tags from configuration files
- **History integration** with common command patterns
This comprehensive CLI reference provides complete coverage of MCPTesta's enterprise-grade capabilities, enabling users to leverage the full power of the testing framework for any FastMCP deployment scenario.

View File

@ -0,0 +1,620 @@
---
title: YAML Configuration Reference
description: Complete YAML configuration format specification for MCPTesta
---
Complete reference for MCPTesta YAML configuration format.
## Schema Overview
```yaml
# Top-level structure
config: # Global configuration settings
variables: # Variable definitions for substitution
servers: # Server connection configurations
test_suites: # Test suite definitions
```
## config
Global configuration settings that apply to all tests.
### Basic Configuration
```yaml
config:
parallel_workers: 4 # Number of parallel test workers
output_directory: "./test_results" # Output directory for reports
output_format: "html" # Output format: console|html|json|junit|all
global_timeout: 300 # Global timeout for all operations (seconds)
max_concurrent_operations: 10 # Maximum concurrent operations per worker
```
### Feature Flags
```yaml
config:
features:
test_notifications: true # Enable notification testing
test_cancellation: true # Enable cancellation testing
test_progress: true # Enable progress monitoring
test_sampling: true # Enable sampling mechanisms
test_auth: false # Enable authentication testing
```
### Advanced Configuration
```yaml
config:
enable_stress_testing: false # Enable stress testing mode
enable_memory_profiling: true # Enable memory usage profiling
enable_performance_profiling: true # Enable performance profiling
continue_on_failure: true # Continue testing after failures
# Retry policy for failed tests
retry_policy:
max_retries: 3 # Maximum retry attempts
backoff_factor: 2.0 # Exponential backoff multiplier
retry_on_errors: ["ConnectionError", "TimeoutError"] # Errors to retry
# Rate limiting configuration
rate_limit:
requests_per_second: 10 # Maximum requests per second
burst_size: 5 # Burst capacity
# Notification configuration
notifications:
enable_resource_changes: true # Monitor resource list changes
enable_tool_changes: true # Monitor tool list changes
enable_prompt_changes: true # Monitor prompt list changes
notification_timeout: 30 # Notification timeout (seconds)
# Logging configuration
logging:
level: "INFO" # Log level: DEBUG|INFO|WARNING|ERROR
console_output: true # Enable console logging
file_output: "./test.log" # Log file path
use_rich_console: true # Use Rich formatting
rich_tracebacks: true # Enhanced error tracebacks
# Metadata for reports
metadata:
environment: "development" # Environment name
service: "my-fastmcp-service" # Service name
version: "1.0.0" # Service version
test_suite_id: "integration-v1" # Test suite identifier
```
## variables
Variable definitions for template substitution throughout the configuration.
```yaml
variables:
# Basic variables
SERVER_CMD: "python my_fastmcp_server.py"
TEST_MESSAGE: "Hello MCPTesta"
TIMEOUT_DEFAULT: 30
PARALLEL_WORKERS: 4
# Environment-specific variables
ENVIRONMENT: "development"
DEBUG_MODE: "true"
LOG_LEVEL: "INFO"
# Authentication variables
AUTH_TOKEN: "${ENV_AUTH_TOKEN}" # From environment variable
API_KEY: "${API_KEY:default_key}" # With default value
# Complex variables
SERVER_URL: "http://localhost:8080"
DATABASE_URL: "sqlite:///test.db"
EXTERNAL_API: "https://api.example.com"
```
### Variable Substitution
Variables can be used anywhere in the configuration:
```yaml
# Basic substitution
server_command: "${SERVER_CMD}"
# With default values
timeout: "${TIMEOUT_DEFAULT:30}"
# Environment variable fallback
auth_token: "${AUTH_TOKEN:${ENV_AUTH_TOKEN:fallback_token}}"
# Nested substitution
database_url: "${DB_PROTOCOL}://${DB_HOST}:${DB_PORT}/${DB_NAME}"
```
## servers
Server connection configurations defining how to connect to FastMCP servers.
### Basic Server Configuration
```yaml
servers:
- name: "my_server" # Unique server identifier
command: "python my_fastmcp_server.py" # Server startup command
transport: "stdio" # Transport: stdio|sse|ws
timeout: 30 # Connection timeout (seconds)
enabled: true # Enable/disable this server
```
### stdio Transport
```yaml
servers:
- name: "stdio_server"
command: "python my_server.py"
transport: "stdio"
timeout: 30
working_directory: "/path/to/server" # Working directory for server process
env_vars: # Environment variables for server
DEBUG: "1"
LOG_LEVEL: "INFO"
DATABASE_URL: "${DATABASE_URL}"
```
### SSE Transport
```yaml
servers:
- name: "sse_server"
command: "http://localhost:8080/mcp" # SSE endpoint URL
transport: "sse"
timeout: 30
headers: # HTTP headers
"User-Agent": "MCPTesta/1.0.0"
"Authorization": "Bearer ${AUTH_TOKEN}"
"X-Custom-Header": "custom-value"
verify_ssl: true # Verify SSL certificates
```
### WebSocket Transport
```yaml
servers:
- name: "ws_server"
command: "ws://localhost:8081/mcp" # WebSocket URL
transport: "ws"
timeout: 30
headers: # WebSocket headers
"Authorization": "Bearer ${AUTH_TOKEN}"
subprotocols: ["mcp-v1"] # WebSocket subprotocols
```
### Authentication Configuration
```yaml
servers:
- name: "authenticated_server"
command: "https://api.example.com/mcp"
transport: "sse"
auth_config:
type: "bearer" # Auth type: bearer|oauth|basic
token: "${AUTH_TOKEN}"
- name: "oauth_server"
command: "https://oauth-server.com/mcp"
transport: "sse"
auth_config:
type: "oauth"
client_id: "${OAUTH_CLIENT_ID}"
client_secret: "${OAUTH_CLIENT_SECRET}"
token_url: "${OAUTH_TOKEN_URL}"
scope: "mcp:read mcp:write"
- name: "basic_auth_server"
command: "https://basic-server.com/mcp"
transport: "sse"
auth_config:
type: "basic"
username: "${BASIC_USERNAME}"
password: "${BASIC_PASSWORD}"
```
## test_suites
Test suite definitions containing groups of related tests.
### Basic Test Suite
```yaml
test_suites:
- name: "Basic Tests" # Suite name
description: "Basic connectivity tests" # Optional description
enabled: true # Enable/disable suite
tags: ["basic", "connectivity"] # Tags for filtering
parallel: true # Enable parallel execution within suite
timeout: 60 # Suite-level timeout
server: "my_server" # Default server for this suite
```
### Suite Setup and Teardown
```yaml
test_suites:
- name: "Advanced Suite"
setup: # Setup operations before suite
validate_connection: true # Validate server connection
discover_capabilities: true # Discover server capabilities
clear_cache: true # Clear any caches
teardown: # Cleanup operations after suite
clear_cache: true # Clear caches
close_connections: true # Close all connections
save_logs: true # Save server logs
```
### Test Definitions
```yaml
test_suites:
- name: "Tool Tests"
tests:
- name: "basic_test" # Test name (must be unique within suite)
description: "Test basic functionality" # Optional description
test_type: "tool_call" # Test type
target: "echo" # Test target (tool/resource/prompt name)
server: "specific_server" # Override default server
timeout: 10 # Test-specific timeout
enabled: true # Enable/disable test
tags: ["tools", "basic"] # Tags for filtering
```
## Test Types
### ping
Test basic connectivity to the server.
```yaml
- name: "connectivity_test"
test_type: "ping"
timeout: 5 # Ping timeout
count: 10 # Number of ping attempts
interval: 1.0 # Interval between pings
```
### tool_call
Test tool execution with parameter validation.
```yaml
- name: "tool_test"
test_type: "tool_call"
target: "calculator" # Tool name
parameters: # Tool parameters
operation: "add"
a: 10
b: 5
expected: # Expected response validation
result: 15
type: "number"
expected_error: null # Expected error message (for error tests)
timeout: 30
```
### resource_read
Test resource access and content validation.
```yaml
- name: "resource_test"
test_type: "resource_read"
target: "config://server.json" # Resource URI
expected: # Expected response validation
content_type: "application/json"
content_length: ">100" # Comparison operators: >, <, >=, <=, ==, !=
content_contains: "server_name" # String containment check
timeout: 15
```
### prompt_get
Test prompt generation and templating.
```yaml
- name: "prompt_test"
test_type: "prompt_get"
target: "greeting" # Prompt name
parameters: # Prompt arguments
name: "MCPTesta User"
language: "en"
expected: # Expected response validation
messages_count: ">0"
message_type: "text"
timeout: 20
```
### notification
Test notification subscription and monitoring.
```yaml
- name: "notification_test"
test_type: "notification"
target: "resources_list_changed" # Notification type
trigger_action: # Action to trigger notification
type: "tool_call"
target: "update_resources"
timeout: 30
max_notifications: 5 # Maximum notifications to wait for
```
## Advanced Test Features
### Progress Monitoring
```yaml
- name: "progress_test"
test_type: "tool_call"
target: "long_running_task"
parameters:
duration: 10
enable_progress: true # Enable progress monitoring
progress_interval: 1.0 # Progress update interval
timeout: 15
```
### Request Cancellation
```yaml
- name: "cancellation_test"
test_type: "tool_call"
target: "slow_task"
parameters:
duration: 60
enable_cancellation: true # Enable cancellation testing
cancel_after: 5 # Cancel after N seconds
timeout: 10
```
### Sampling Mechanisms
```yaml
- name: "sampling_test"
test_type: "tool_call"
target: "echo"
parameters:
message: "sampling test"
enable_sampling: true # Enable sampling
sampling_rate: 0.5 # Sampling rate (0.0 to 1.0)
sample_method: "random" # Sampling method: random|round_robin|weighted
timeout: 10
```
### Test Dependencies
```yaml
test_suites:
- name: "Dependency Example"
tests:
- name: "setup_test"
test_type: "tool_call"
target: "initialize"
- name: "main_test"
test_type: "tool_call"
target: "main_function"
depends_on: ["setup_test"] # Run after setup_test
- name: "cleanup_test"
test_type: "tool_call"
target: "cleanup"
depends_on: ["main_test"] # Run after main_test
- name: "parallel_test_1"
test_type: "tool_call"
target: "parallel_function_1"
depends_on: ["setup_test"] # Runs in parallel with parallel_test_2
- name: "parallel_test_2"
test_type: "tool_call"
target: "parallel_function_2"
depends_on: ["setup_test"] # Runs in parallel with parallel_test_1
```
### Retry Configuration
```yaml
- name: "retry_test"
test_type: "tool_call"
target: "unreliable_tool"
retry_count: 3 # Number of retry attempts
retry_delay: 2.0 # Delay between retries
retry_backoff: 1.5 # Backoff multiplier
retry_on_errors: ["TimeoutError"] # Specific errors to retry
```
### Conditional Testing
```yaml
- name: "conditional_test"
test_type: "tool_call"
target: "conditional_tool"
conditions: # Conditions for test execution
environment: "development" # Only run in development
server_has_tool: "conditional_tool" # Only if server has this tool
previous_test_passed: "setup_test" # Only if setup_test passed
skip_reason: "Only for development" # Reason for skipping
```
## Validation and Assertions
### Response Validation
```yaml
expected:
# Exact matching
field_name: "expected_value"
# Type checking
numeric_field: { type: "number" }
string_field: { type: "string" }
array_field: { type: "array" }
object_field: { type: "object" }
# Comparison operators
count: { ">": 0 } # Greater than
score: { ">=": 85 } # Greater than or equal
size: { "<": 1000 } # Less than
limit: { "<=": 100 } # Less than or equal
status: { "!=": "error" } # Not equal
# String operations
message: { contains: "success" } # String contains
text: { matches: "^Hello.*" } # Regex matching
name: { starts_with: "test_" } # String starts with
file: { ends_with: ".json" } # String ends with
# Array operations
items: { length: 5 } # Array length
tags: { contains_all: ["tag1", "tag2"] } # Contains all elements
options: { contains_any: ["opt1", "opt2"] } # Contains any element
# Nested object validation
metadata:
version: "1.0.0"
author: { type: "string" }
created: { matches: "\\d{4}-\\d{2}-\\d{2}" }
```
### Error Validation
```yaml
- name: "error_test"
test_type: "tool_call"
target: "failing_tool"
parameters:
invalid_param: "bad_value"
expected_error:
message: "Invalid parameter" # Error message matching
type: "ValueError" # Error type
code: 400 # Error code
details: { contains: "invalid_param" } # Error details validation
```
## Performance and Load Testing
### Stress Testing Configuration
```yaml
- name: "stress_test"
test_type: "tool_call"
target: "performance_tool"
parameters:
data_size: 10000
stress_config:
concurrent_requests: 50 # Concurrent requests
total_requests: 1000 # Total requests to send
ramp_up_time: 10 # Ramp-up period (seconds)
duration: 60 # Test duration (seconds)
think_time: 0.1 # Delay between requests
performance_thresholds:
max_response_time: 5.0 # Maximum response time (seconds)
min_throughput: 100 # Minimum requests per second
max_error_rate: 0.01 # Maximum error rate (0.0 to 1.0)
```
### Memory and Performance Profiling
```yaml
- name: "profiling_test"
test_type: "tool_call"
target: "memory_intensive_tool"
enable_memory_profiling: true # Enable memory profiling
enable_performance_profiling: true # Enable performance profiling
profiling_config:
sample_interval: 0.1 # Profiling sample interval
track_allocations: true # Track memory allocations
capture_stack_traces: true # Capture stack traces
```
## Examples
### Complete Configuration Example
```yaml
# Global configuration
config:
parallel_workers: 4
output_directory: "./comprehensive_results"
output_format: "html"
global_timeout: 300
features:
test_notifications: true
test_cancellation: true
test_progress: true
test_sampling: true
# Variables
variables:
SERVER_CMD: "python demo_server.py"
TEST_ENV: "integration"
AUTH_TOKEN: "${ENV_AUTH_TOKEN:default_token}"
# Server configurations
servers:
- name: "primary_server"
command: "${SERVER_CMD}"
transport: "stdio"
timeout: 30
env_vars:
ENVIRONMENT: "${TEST_ENV}"
DEBUG: "true"
# Test suites
test_suites:
- name: "Integration Tests"
description: "Comprehensive integration testing"
parallel: true
tags: ["integration", "comprehensive"]
setup:
validate_connection: true
discover_capabilities: true
tests:
- name: "connectivity_test"
test_type: "ping"
timeout: 5
tags: ["connectivity"]
- name: "tool_discovery"
test_type: "tool_call"
target: "list_tools"
expected:
tools_count: { ">": 0 }
depends_on: ["connectivity_test"]
tags: ["discovery"]
- name: "advanced_tool_test"
test_type: "tool_call"
target: "complex_operation"
parameters:
input_data: "test_data"
options:
verbose: true
timeout: 30
expected:
status: "success"
result: { type: "object" }
metadata:
processing_time: { "<": 5.0 }
enable_progress: true
enable_cancellation: true
depends_on: ["tool_discovery"]
tags: ["tools", "advanced"]
teardown:
clear_cache: true
close_connections: true
```
This reference covers all available configuration options. For specific use cases and examples, see the [How-to Guides](/how-to/) and [Tutorials](/tutorials/) sections.

View File

@ -0,0 +1,162 @@
---
title: Your First Test
description: Step-by-step tutorial to run your first MCPTesta test against a FastMCP server
---
In this tutorial, we'll run your first MCPTesta test against a FastMCP server. You'll learn the basic workflow and see immediate results.
:::note[What you'll build]
By the end of this tutorial, you'll have:
- Tested a FastMCP server with MCPTesta
- Seen a successful test report
- Understood the basic testing workflow
:::
## Before we start
Make sure you have:
- MCPTesta installed ([Installation guide](/installation/))
- A FastMCP server to test (we'll help you create one if needed)
## Step 1: Get a test server
We'll use a simple echo server for your first test. Create a file called `echo_server.py`:
```python
from fastmcp import FastMCP
mcp = FastMCP("Echo Server")
@mcp.tool()
def echo(message: str) -> str:
"""Echo back the message you send."""
return f"Echo: {message}"
if __name__ == "__main__":
mcp.run()
```
This creates a FastMCP server with one tool that echoes messages back to you.
## Step 2: Test connectivity
Let's verify we can connect to our server:
```bash
mcptesta ping --server "python echo_server.py"
```
You should see output like:
```
🏓 Pinging server: python echo_server.py
📊 Ping Statistics:
Sent: 100
Received: 100
Lost: 0 (0.0%)
Min: 2.34ms
Max: 8.91ms
Avg: 4.12ms
```
Great! Our server is responding.
## Step 3: Run your first test
Now let's test the echo tool:
```bash
mcptesta test --server "python echo_server.py"
```
MCPTesta will automatically discover your server's capabilities and run basic tests. You'll see:
```
🚀 Testing FastMCP server: python echo_server.py
✅ Server connection successful
🔧 Tools: 1 available
• echo
📊 Test Execution Summary
Tests run: 3
Passed: 3
Failed: 0
Skipped: 0
Execution time: 2.14s
```
Perfect! All tests passed.
## Step 4: Test the echo tool specifically
Let's test the echo tool with a custom message:
```bash
mcptesta test \
--server "python echo_server.py" \
--include-tools "echo"
```
This runs targeted tests on just the echo tool, validating that it works correctly with different inputs.
## What just happened?
MCPTesta automatically:
1. **Connected** to your FastMCP server
2. **Discovered** available tools (found the `echo` tool)
3. **Tested connectivity** with ping tests
4. **Validated tools** by calling them with test data
5. **Reported results** with a clear summary
The tests passed because your echo server is working correctly and responding to MCP protocol requests.
## Your first success
Congratulations! You've successfully:
- ✅ Connected MCPTesta to a FastMCP server
- ✅ Run automated tests against your server
- ✅ Seen a passing test report
## What's next?
Now that you've run your first test, you're ready to:
- [Explore more testing capabilities](/tutorials/testing-walkthrough/) - Learn about advanced features
- [Create YAML test configurations](/tutorials/yaml-configuration/) - Build more complex test scenarios
- [Test real FastMCP servers](/how-to/test-production-servers/) - Apply testing to actual projects
## Troubleshooting
**Server won't start?** Make sure you have FastMCP installed:
```bash
pip install fastmcp
```
**Connection errors?** Verify your server is running:
```bash
python echo_server.py
# Should show server startup messages
```
**No tests found?** Make sure your server exposes tools correctly - MCPTesta automatically discovers and tests available tools.
## What's next?
Now that you've run your first test, here are the logical next steps to master MCPTesta:
### **Learning Path (Tutorials)**
- **[Testing Walkthrough](/tutorials/testing-walkthrough/)**: Explore all MCPTesta capabilities with comprehensive examples
- **[YAML Configuration](/tutorials/yaml-configuration/)**: Master configuration files for complex testing scenarios
- **[Parallel Testing](/tutorials/parallel-testing/)**: Scale your testing with intelligent parallel execution
### **Practical Applications (How-to Guides)**
- **[CI/CD Integration](/how-to/ci-cd-integration/)**: Automate testing in your development pipeline
- **[Container Testing](/how-to/container-testing/)**: Test FastMCP servers in Docker and Kubernetes
- **[Troubleshooting](/how-to/troubleshooting/)**: Debug common testing issues
### **Understanding the Framework (Explanations)**
- **[MCP Protocol Testing](/explanation/mcp-protocol/)**: Understand what makes MCP testing unique
- **[Architecture Overview](/explanation/architecture/)**: Learn about MCPTesta's enterprise-grade design
### **Quick Reference (Reference)**
- **[CLI Reference](/reference/cli/)**: Complete command-line interface documentation
- **[YAML Reference](/reference/yaml/)**: Full configuration file specification

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,268 @@
---
title: Testing Walkthrough
description: Comprehensive walkthrough of all MCPTesta testing capabilities and features
---
In this tutorial, we'll explore MCPTesta's testing capabilities by building a comprehensive test suite. You'll learn how to test tools, resources, prompts, and advanced MCP protocol features.
:::note[What you'll build]
By the end of this tutorial, you'll have:
- Tested all major MCP capabilities
- Used both CLI and YAML configurations
- Seen advanced protocol features in action
- Created reusable test configurations
:::
## Before we start
Complete the [Your First Test](/tutorials/first-test/) tutorial, or ensure you have:
- MCPTesta installed and working
- A FastMCP server to test (we'll enhance the echo server)
## Step 1: Create a richer test server
Let's build a FastMCP server with multiple capabilities:
```python
from fastmcp import FastMCP
import time
import asyncio
mcp = FastMCP("Demo Server")
@mcp.tool()
def echo(message: str) -> str:
"""Echo back the message."""
return f"Echo: {message}"
@mcp.tool()
def calculator(operation: str, a: float, b: float) -> float:
"""Perform basic calculations."""
if operation == "add":
return a + b
elif operation == "subtract":
return a - b
elif operation == "multiply":
return a * b
elif operation == "divide":
if b == 0:
raise ValueError("Division by zero")
return a / b
else:
raise ValueError(f"Unknown operation: {operation}")
@mcp.tool()
async def slow_task(seconds: int = 5) -> str:
"""A task that takes time to complete."""
await asyncio.sleep(seconds)
return f"Task completed after {seconds} seconds"
@mcp.resource("config://server.json")
def get_server_config():
"""Server configuration resource."""
return {
"name": "Demo Server",
"version": "1.0.0",
"features": ["tools", "resources", "prompts"]
}
@mcp.prompt()
def greeting(name: str = "User") -> str:
"""Generate a greeting message."""
return f"Hello, {name}! Welcome to our demo server."
if __name__ == "__main__":
mcp.run()
```
Save this as `demo_server.py`.
## Step 2: Test basic capabilities
Let's start by testing all the basic capabilities:
```bash
mcptesta test --server "python demo_server.py"
```
You'll see MCPTesta discover and test:
- ✅ 3 tools (echo, calculator, slow_task)
- ✅ 1 resource (config://server.json)
- ✅ 1 prompt (greeting)
The output shows what MCPTesta found and tested automatically.
## Step 3: Test specific scenarios
Now let's test specific scenarios with CLI parameters:
### Test the calculator tool
```bash
mcptesta test \
--server "python demo_server.py" \
--include-tools "calculator"
```
### Test error handling
```bash
mcptesta test \
--server "python demo_server.py" \
--include-tools "calculator" \
--stress-test
```
The stress test will try various inputs including edge cases that should trigger errors.
## Step 4: Create a YAML test configuration
Now we'll create a comprehensive test configuration. Create `demo_tests.yaml`:
```yaml
config:
parallel_workers: 2
output_format: "console"
features:
test_notifications: true
test_progress: true
test_cancellation: true
servers:
- name: "demo_server"
command: "python demo_server.py"
transport: "stdio"
timeout: 30
test_suites:
- name: "Basic Tool Tests"
parallel: true
tests:
- name: "echo_test"
test_type: "tool_call"
target: "echo"
parameters:
message: "Hello MCPTesta!"
expected:
result: "Echo: Hello MCPTesta!"
- name: "calculator_add"
test_type: "tool_call"
target: "calculator"
parameters:
operation: "add"
a: 10
b: 5
expected:
result: 15
- name: "calculator_divide_by_zero"
test_type: "tool_call"
target: "calculator"
parameters:
operation: "divide"
a: 10
b: 0
expected_error: "Division by zero"
- name: "Resource Tests"
tests:
- name: "server_config"
test_type: "resource_read"
target: "config://server.json"
expected:
content_type: "application/json"
- name: "Prompt Tests"
tests:
- name: "greeting_default"
test_type: "prompt_get"
target: "greeting"
- name: "greeting_custom"
test_type: "prompt_get"
target: "greeting"
parameters:
name: "MCPTesta"
- name: "Advanced Features"
parallel: false
tests:
- name: "progress_test"
test_type: "tool_call"
target: "slow_task"
parameters:
seconds: 3
enable_progress: true
timeout: 10
- name: "cancellation_test"
test_type: "tool_call"
target: "slow_task"
parameters:
seconds: 30
enable_cancellation: true
timeout: 5 # Will trigger cancellation
```
## Step 5: Run your comprehensive tests
Execute your YAML configuration:
```bash
mcptesta yaml demo_tests.yaml
```
Watch as MCPTesta runs through all your test cases:
- ✅ **Basic tool tests** run in parallel
- ✅ **Error handling** validates expected failures
- ✅ **Resource tests** verify content access
- ✅ **Prompt tests** check template generation
- ✅ **Progress monitoring** shows real-time updates
- ✅ **Cancellation testing** demonstrates request interruption
## Step 6: Generate detailed reports
Create HTML reports for sharing:
```bash
mcptesta yaml demo_tests.yaml --format html --output ./reports
```
This creates a comprehensive HTML report with:
- Test execution timeline
- Success/failure details
- Performance metrics
- Error details and stack traces
## What you've learned
In this walkthrough, you've:
- ✅ **Tested multiple MCP capabilities** - tools, resources, prompts
- ✅ **Used CLI and YAML configurations** - both testing approaches
- ✅ **Validated error handling** - expected failures work correctly
- ✅ **Explored advanced features** - progress monitoring and cancellation
- ✅ **Generated detailed reports** - shareable test documentation
## Key concepts
**Automatic discovery**: MCPTesta finds and tests all server capabilities automatically.
**Expected failures**: Use `expected_error` to test that errors are handled correctly.
**Progress monitoring**: Enable `enable_progress: true` for long-running operations.
**Parallel testing**: Tests run in parallel unless they have dependencies.
**Multiple formats**: Generate reports in console, HTML, JSON, or JUnit formats.
## What's next?
Now that you understand MCPTesta's capabilities:
- [Learn YAML configuration in depth](/tutorials/yaml-configuration/) - Master advanced configurations
- [Set up parallel testing](/tutorials/parallel-testing/) - Scale your testing
- [Test production servers](/how-to/test-production-servers/) - Apply to real projects
- [Integrate with CI/CD](/how-to/ci-cd-integration/) - Automate your testing
You're ready to build comprehensive test suites for any FastMCP server!

View File

@ -0,0 +1,850 @@
---
title: YAML Configuration
description: Master YAML configurations for comprehensive test scenarios with MCPTesta
---
This tutorial will teach you how to create comprehensive YAML configurations for MCPTesta, progressing from basic setups to enterprise-grade test scenarios with advanced MCP protocol features.
:::note[What you'll build]
By the end of this tutorial, you'll have:
- Created a complete YAML test configuration with all MCPTesta features
- Mastered variables, dependencies, and conditional testing
- Configured advanced MCP protocol features (notifications, progress, cancellation, sampling)
- Built reusable test templates for different complexity levels
- Implemented enterprise patterns for production testing
:::
## Before we start
Complete the [Testing Walkthrough](/tutorials/testing-walkthrough/) tutorial, or ensure you have:
- MCPTesta installed and working
- Understanding of basic testing concepts
- A FastMCP server to test (we'll enhance the demo server from previous tutorials)
## Step 1: Create your first YAML configuration
Let's start with a foundation that demonstrates the core YAML structure. Create a file called `basic_tests.yaml`:
```yaml
# Basic MCPTesta configuration demonstrating core structure
config:
parallel_workers: 2
output_format: "console"
global_timeout: 60
servers:
- name: "test_server"
command: "python demo_server.py"
transport: "stdio"
timeout: 30
test_suites:
- name: "Foundation Tests"
description: "Basic connectivity and functionality validation"
tests:
- name: "connectivity_ping"
test_type: "ping"
timeout: 5
- name: "echo_validation"
test_type: "tool_call"
target: "echo"
parameters:
message: "Hello YAML Configuration!"
expected:
result: "Echo: Hello YAML Configuration!"
timeout: 10
```
Run this configuration to verify your foundation:
```bash
mcptesta yaml basic_tests.yaml
```
You should see MCPTesta execute both tests successfully with clear output showing the YAML-driven execution.
## Step 2: Master variables and template substitution
Variables are crucial for maintainable configurations. Let's enhance our setup with sophisticated variable usage:
```yaml
# Advanced variable usage demonstrating all substitution patterns
variables:
# Basic variables
SERVER_COMMAND: "python demo_server.py"
TEST_MESSAGE: "Hello MCPTesta YAML!"
DEFAULT_TIMEOUT: 15
PARALLEL_WORKERS: 2
# Environment-based variables with fallbacks
ENVIRONMENT: "${ENV_ENVIRONMENT:development}"
DEBUG_MODE: "${ENV_DEBUG:true}"
LOG_LEVEL: "${ENV_LOG_LEVEL:INFO}"
# Computed variables
OUTPUT_DIR: "./results_${ENVIRONMENT}"
SERVER_URL: "http://localhost:${SERVER_PORT:8080}"
# Complex data for testing
LARGE_PAYLOAD_SIZE: 10000
PERFORMANCE_ITERATIONS: 50
config:
parallel_workers: "${PARALLEL_WORKERS}"
output_format: "html"
output_directory: "${OUTPUT_DIR}"
global_timeout: 120
# Metadata for reporting
metadata:
environment: "${ENVIRONMENT}"
test_suite_version: "1.0.0"
created_by: "MCPTesta YAML Tutorial"
servers:
- name: "demo_server"
command: "${SERVER_COMMAND}"
transport: "stdio"
timeout: "${DEFAULT_TIMEOUT}"
env_vars:
ENVIRONMENT: "${ENVIRONMENT}"
DEBUG: "${DEBUG_MODE}"
LOG_LEVEL: "${LOG_LEVEL}"
test_suites:
- name: "Variable Demonstration"
description: "Showcasing variable substitution patterns"
tags: ["variables", "demonstration"]
tests:
- name: "environment_check"
test_type: "tool_call"
target: "echo"
parameters:
message: "Running in ${ENVIRONMENT} mode"
debug: "${DEBUG_MODE}"
expected:
result: "Echo: Running in ${ENVIRONMENT} mode"
timeout: "${DEFAULT_TIMEOUT}"
- name: "payload_size_test"
test_type: "tool_call"
target: "echo"
parameters:
large_data: "${ ''.join(['x'] * ${LARGE_PAYLOAD_SIZE}) }"
timeout: 30
```
Notice how we use multiple variable patterns:
- Basic substitution: `${VARIABLE_NAME}`
- Default values: `${VAR:default_value}`
- Environment fallbacks: `${ENV_VAR:fallback}`
- Computed expressions: Complex string operations
## Step 3: Implement sophisticated test dependencies
Dependencies control execution flow while maintaining parallelism where possible. Let's create a complex dependency chain:
```yaml
variables:
SERVER_COMMAND: "python demo_server.py"
SETUP_DATA: '{"status": "initialized", "timestamp": "2024-01-01T00:00:00Z"}'
config:
parallel_workers: 4
output_format: "console"
features:
test_notifications: true
test_progress: true
servers:
- name: "dependency_server"
command: "${SERVER_COMMAND}"
transport: "stdio"
timeout: 30
test_suites:
- name: "Dependency Chain Example"
description: "Complex dependency relationships with parallel execution"
parallel: true # Enable parallelism where dependencies allow
tests:
# Foundation layer - runs first
- name: "system_initialization"
test_type: "tool_call"
target: "initialize_system"
parameters:
config_data: "${SETUP_DATA}"
timeout: 20
tags: ["foundation", "setup"]
# Parallel dependency layer - both depend on initialization
- name: "database_setup"
test_type: "tool_call"
target: "setup_database"
depends_on: ["system_initialization"]
timeout: 15
tags: ["database", "setup"]
- name: "cache_initialization"
test_type: "tool_call"
target: "initialize_cache"
depends_on: ["system_initialization"]
timeout: 10
tags: ["cache", "setup"]
# Service layer - depends on both database and cache
- name: "service_startup"
test_type: "tool_call"
target: "start_services"
depends_on: ["database_setup", "cache_initialization"]
timeout: 25
tags: ["services", "startup"]
# Validation layer - runs in parallel, all depend on services
- name: "service_health_check"
test_type: "tool_call"
target: "health_check"
depends_on: ["service_startup"]
expected:
status: "healthy"
services_running: { ">": 0 }
tags: ["validation", "health"]
- name: "performance_baseline"
test_type: "tool_call"
target: "performance_test"
depends_on: ["service_startup"]
timeout: 30
tags: ["validation", "performance"]
- name: "integration_test"
test_type: "tool_call"
target: "integration_check"
depends_on: ["service_startup"]
timeout: 20
tags: ["validation", "integration"]
# Cleanup - depends on all validation tests
- name: "system_cleanup"
test_type: "tool_call"
target: "cleanup_system"
depends_on: ["service_health_check", "performance_baseline", "integration_test"]
timeout: 15
tags: ["cleanup", "teardown"]
```
This creates an execution flow:
1. **system_initialization** runs first
2. **database_setup** and **cache_initialization** run in parallel
3. **service_startup** waits for both setup tasks
4. All validation tests run in parallel
5. **system_cleanup** runs after all validations complete
## Step 4: Configure advanced MCP protocol features
Now we'll implement comprehensive testing of advanced MCP protocol capabilities:
```yaml
variables:
SERVER_COMMAND: "python advanced_demo_server.py"
NOTIFICATION_TIMEOUT: 30
PROGRESS_TASK_DURATION: 10
CANCELLATION_TIMEOUT: 5
SAMPLING_ITERATIONS: 25
config:
parallel_workers: 3
output_format: "html"
output_directory: "./advanced_protocol_results"
global_timeout: 180
# Enable all advanced protocol features
features:
test_notifications: true
test_cancellation: true
test_progress: true
test_sampling: true
test_auth: false # Enable if your server supports auth
# Advanced notification configuration
notifications:
enable_resource_changes: true
enable_tool_changes: true
enable_prompt_changes: true
notification_timeout: "${NOTIFICATION_TIMEOUT}"
servers:
- name: "advanced_protocol_server"
command: "${SERVER_COMMAND}"
transport: "stdio"
timeout: 45
env_vars:
ENABLE_NOTIFICATIONS: "true"
ENABLE_PROGRESS: "true"
ENABLE_CANCELLATION: "true"
test_suites:
- name: "Notification System Tests"
description: "Comprehensive notification feature testing"
tags: ["notifications", "protocol", "advanced"]
parallel: false # Sequential for notification timing
tests:
- name: "resource_list_notification"
description: "Test resource list change notifications"
test_type: "notification"
target: "resources_list_changed"
trigger_action:
type: "tool_call"
target: "add_resource"
parameters:
resource_name: "test_resource_${timestamp}"
resource_type: "dynamic"
timeout: "${NOTIFICATION_TIMEOUT}"
tags: ["notifications", "resources"]
- name: "tool_list_notification"
description: "Test tool list change notifications"
test_type: "notification"
target: "tools_list_changed"
trigger_action:
type: "tool_call"
target: "register_dynamic_tool"
parameters:
tool_name: "dynamic_tool_${timestamp}"
timeout: "${NOTIFICATION_TIMEOUT}"
tags: ["notifications", "tools"]
- name: "Progress Monitoring Tests"
description: "Real-time progress reporting validation"
tags: ["progress", "monitoring", "real-time"]
tests:
- name: "long_running_task_progress"
description: "Monitor progress of extended operation"
test_type: "tool_call"
target: "long_running_task"
parameters:
duration: "${PROGRESS_TASK_DURATION}"
report_interval: 1
task_type: "data_processing"
enable_progress: true
progress_interval: 0.5 # Check progress every 500ms
timeout: 15
expected:
status: "completed"
progress_reports: { ">": 5 }
tags: ["progress", "long_running"]
- name: "parallel_progress_monitoring"
description: "Monitor multiple concurrent progress streams"
test_type: "tool_call"
target: "parallel_tasks"
parameters:
task_count: 3
task_duration: 8
enable_progress: true
timeout: 12
tags: ["progress", "parallel"]
- name: "Cancellation Mechanism Tests"
description: "Request cancellation and cleanup validation"
tags: ["cancellation", "cleanup", "interruption"]
tests:
- name: "graceful_cancellation"
description: "Test graceful operation cancellation"
test_type: "tool_call"
target: "cancellable_task"
parameters:
duration: 60 # Long enough to guarantee cancellation
cleanup_on_cancel: true
enable_cancellation: true
cancel_after: "${CANCELLATION_TIMEOUT}"
timeout: 8
expected_cancellation_behavior:
cleanup_performed: true
partial_results_saved: true
tags: ["cancellation", "graceful"]
- name: "immediate_cancellation"
description: "Test immediate cancellation response"
test_type: "tool_call"
target: "immediate_cancel_task"
enable_cancellation: true
cancel_after: 1 # Cancel almost immediately
timeout: 3
tags: ["cancellation", "immediate"]
- name: "Sampling Mechanism Tests"
description: "Request sampling and throttling validation"
tags: ["sampling", "throttling", "load_management"]
tests:
- name: "random_sampling_test"
description: "Test random sampling mechanism"
test_type: "tool_call"
target: "echo"
parameters:
message: "Sampling test ${iteration}"
enable_sampling: true
sampling_rate: 0.6 # 60% sampling rate
sample_method: "random"
retry_count: "${SAMPLING_ITERATIONS}"
expected_sample_behavior:
samples_taken: { ">=": 10, "<=": 20 } # Expect ~15 samples
tags: ["sampling", "random"]
- name: "adaptive_sampling_test"
description: "Test adaptive sampling under load"
test_type: "tool_call"
target: "load_sensitive_operation"
enable_sampling: true
sampling_rate: 0.8
sample_method: "adaptive"
retry_count: 30
tags: ["sampling", "adaptive"]
```
## Step 5: Build enterprise-grade multi-suite configuration
Now let's create a comprehensive configuration suitable for production environments:
```yaml
# Enterprise-grade MCPTesta configuration
# Comprehensive testing with full feature coverage
variables:
# Environment configuration
ENVIRONMENT: "${ENV_ENVIRONMENT:staging}"
SERVER_COMMAND: "python production_server.py"
API_BASE_URL: "${ENV_API_BASE_URL:http://localhost:8080}"
AUTH_TOKEN: "${ENV_AUTH_TOKEN}"
# Performance parameters
PARALLEL_WORKERS: 6
STRESS_TEST_DURATION: 120
PERFORMANCE_ITERATIONS: 200
LARGE_PAYLOAD_SIZE: 50000
# Timeout configuration
FAST_TIMEOUT: 5
STANDARD_TIMEOUT: 15
SLOW_TIMEOUT: 30
STRESS_TIMEOUT: 180
# Test data
TEST_USER_ID: "test_user_${timestamp}"
TEST_SESSION_ID: "session_${random_id}"
config:
parallel_workers: "${PARALLEL_WORKERS}"
output_directory: "./enterprise_results_${ENVIRONMENT}"
output_format: "all" # Generate all formats for comprehensive reporting
global_timeout: 600
max_concurrent_operations: 15
# Enterprise feature configuration
enable_stress_testing: true
enable_memory_profiling: true
enable_performance_profiling: true
continue_on_failure: true
features:
test_notifications: true
test_cancellation: true
test_progress: true
test_sampling: true
test_auth: true
# Enterprise retry policy
retry_policy:
max_retries: 3
backoff_factor: 2.0
retry_on_errors: ["ConnectionError", "TimeoutError", "ServiceUnavailable"]
# Rate limiting for production safety
rate_limit:
requests_per_second: 20
burst_size: 10
# Comprehensive logging
logging:
level: "INFO"
console_output: true
file_output: "./enterprise_test.log"
use_rich_console: true
rich_tracebacks: true
# Metadata for enterprise reporting
metadata:
environment: "${ENVIRONMENT}"
service: "fastmcp-production-service"
version: "2.1.0"
test_suite_id: "enterprise-comprehensive-v1"
compliance_framework: "SOC2"
security_level: "production"
servers:
- name: "primary_production_server"
command: "${SERVER_COMMAND}"
transport: "stdio"
timeout: "${STANDARD_TIMEOUT}"
working_directory: "/app/server"
env_vars:
ENVIRONMENT: "${ENVIRONMENT}"
LOG_LEVEL: "INFO"
DATABASE_URL: "${ENV_DATABASE_URL}"
CACHE_URL: "${ENV_CACHE_URL}"
API_KEY: "${ENV_API_KEY}"
auth_config:
type: "bearer"
token: "${AUTH_TOKEN}"
- name: "load_balanced_server"
command: "${API_BASE_URL}/mcp"
transport: "sse"
timeout: "${SLOW_TIMEOUT}"
headers:
"Authorization": "Bearer ${AUTH_TOKEN}"
"User-Agent": "MCPTesta-Enterprise/1.0.0"
"X-Environment": "${ENVIRONMENT}"
verify_ssl: true
test_suites:
- name: "Infrastructure Validation"
description: "Core infrastructure and connectivity validation"
enabled: true
tags: ["infrastructure", "connectivity", "critical"]
parallel: true
timeout: 90
setup:
validate_connection: true
discover_capabilities: true
verify_authentication: true
tests:
- name: "multi_server_connectivity"
description: "Validate connectivity to all configured servers"
test_type: "ping"
timeout: "${FAST_TIMEOUT}"
count: 20
interval: 0.5
tags: ["connectivity", "critical"]
- name: "authentication_validation"
description: "Verify authentication mechanisms"
test_type: "tool_call"
target: "auth_check"
expected:
authenticated: true
user_id: { type: "string" }
permissions: { type: "array" }
tags: ["auth", "security", "critical"]
- name: "capability_discovery"
description: "Discover and validate server capabilities"
test_type: "tool_call"
target: "list_capabilities"
expected:
tools_count: { ">": 10 }
resources_count: { ">": 5 }
prompts_count: { ">": 3 }
tags: ["discovery", "capabilities"]
- name: "Functional Test Suite"
description: "Comprehensive business logic validation"
enabled: true
tags: ["functional", "business_logic", "core"]
parallel: true
timeout: 180
tests:
- name: "user_session_management"
description: "Test user session creation and management"
test_type: "tool_call"
target: "create_user_session"
parameters:
user_id: "${TEST_USER_ID}"
session_type: "test"
metadata:
test_run: true
environment: "${ENVIRONMENT}"
expected:
session_id: { type: "string" }
status: "active"
expires_at: { type: "string" }
timeout: "${STANDARD_TIMEOUT}"
tags: ["sessions", "users"]
- name: "data_processing_pipeline"
description: "Test end-to-end data processing"
test_type: "tool_call"
target: "process_data"
parameters:
data_source: "test_dataset"
processing_options:
format: "json"
validation: true
enrichment: true
enable_progress: true
expected:
status: "completed"
records_processed: { ">": 100 }
error_count: { "<=": 5 }
timeout: "${SLOW_TIMEOUT}"
tags: ["data_processing", "pipeline"]
depends_on: ["user_session_management"]
- name: "Performance and Load Testing"
description: "Performance benchmarks and stress testing"
enabled: true
tags: ["performance", "load", "benchmarks"]
parallel: false # Sequential for accurate performance measurement
timeout: 300
tests:
- name: "baseline_performance"
description: "Establish performance baseline metrics"
test_type: "tool_call"
target: "performance_benchmark"
parameters:
test_type: "baseline"
iterations: 100
stress_config:
concurrent_requests: 1
total_requests: 100
duration: 60
performance_thresholds:
max_response_time: 2.0
min_throughput: 50
max_error_rate: 0.01
tags: ["performance", "baseline"]
- name: "concurrent_load_test"
description: "Test system under concurrent load"
test_type: "tool_call"
target: "echo"
parameters:
message: "Load test ${iteration}"
stress_config:
concurrent_requests: 25
total_requests: "${PERFORMANCE_ITERATIONS}"
ramp_up_time: 15
duration: "${STRESS_TEST_DURATION}"
performance_thresholds:
max_response_time: 5.0
min_throughput: 30
max_error_rate: 0.05
enable_memory_profiling: true
enable_performance_profiling: true
tags: ["performance", "concurrent", "load"]
- name: "large_payload_handling"
description: "Test large payload processing capabilities"
test_type: "tool_call"
target: "process_large_data"
parameters:
payload_size: "${LARGE_PAYLOAD_SIZE}"
data: "${ ''.join(['A'] * ${LARGE_PAYLOAD_SIZE}) }"
timeout: 60
expected:
processed_bytes: "${LARGE_PAYLOAD_SIZE}"
processing_time: { "<": 30.0 }
tags: ["performance", "payload", "memory"]
- name: "Advanced Protocol Features"
description: "Comprehensive advanced MCP protocol validation"
enabled: true
tags: ["protocol", "advanced", "mcp"]
parallel: false
timeout: 240
tests:
- name: "notification_stress_test"
description: "Test notification system under load"
test_type: "notification"
target: "resources_list_changed"
trigger_action:
type: "tool_call"
target: "bulk_resource_update"
parameters:
resource_count: 50
update_interval: 0.1
timeout: 60
max_notifications: 50
tags: ["notifications", "stress"]
- name: "complex_progress_monitoring"
description: "Monitor complex multi-stage operations"
test_type: "tool_call"
target: "multi_stage_process"
parameters:
stages: ["validation", "processing", "optimization", "output"]
stage_duration: 8
enable_progress: true
progress_interval: 0.5
timeout: 45
expected:
stages_completed: 4
total_progress: 100
tags: ["progress", "complex", "multi_stage"]
- name: "cascading_cancellation"
description: "Test cancellation with cleanup cascade"
test_type: "tool_call"
target: "complex_workflow"
parameters:
workflow_steps: 10
step_duration: 10
enable_cancellation: true
cancel_after: 15
timeout: 25
expected_cancellation_behavior:
cleanup_performed: true
workflow_state_saved: true
resources_released: true
tags: ["cancellation", "workflow", "cleanup"]
- name: "Error Handling and Edge Cases"
description: "Comprehensive error scenario validation"
enabled: true
tags: ["errors", "edge_cases", "resilience"]
parallel: true
timeout: 120
tests:
- name: "invalid_authentication"
description: "Test invalid authentication handling"
test_type: "tool_call"
target: "protected_operation"
auth_config:
type: "bearer"
token: "invalid_token_123"
expected_error:
type: "AuthenticationError"
code: 401
message: { contains: "authentication" }
tags: ["auth", "error", "security"]
- name: "resource_not_found"
description: "Test resource not found error handling"
test_type: "resource_read"
target: "file://./non_existent_file.txt"
expected_error:
type: "ResourceNotFoundError"
message: { contains: "not found" }
tags: ["resources", "error", "not_found"]
- name: "timeout_handling"
description: "Test operation timeout handling"
test_type: "tool_call"
target: "slow_operation"
parameters:
delay: 30
timeout: 5 # Guaranteed timeout
expected_error:
type: "TimeoutError"
message: { contains: "timeout" }
tags: ["timeout", "error", "handling"]
teardown:
clear_cache: true
close_connections: true
save_performance_metrics: true
generate_summary_report: true
```
## Step 6: Execute and analyze your comprehensive configuration
Run your enterprise configuration:
```bash
# Execute with full logging and profiling
mcptesta yaml enterprise_tests.yaml --parallel 6 -vv
# Generate specific format outputs
mcptesta yaml enterprise_tests.yaml --format html --output ./detailed_reports
# Run with filtering for specific test types
mcptesta yaml enterprise_tests.yaml --tag performance --tag critical
```
You'll see comprehensive execution with:
- ✅ **Complex dependency resolution** with optimal parallel execution
- ✅ **Advanced protocol feature testing** with real-time monitoring
- ✅ **Enterprise-grade error handling** and resilience validation
- ✅ **Performance profiling** with detailed metrics
- ✅ **Rich reporting** in multiple formats for different audiences
## Step 7: Master configuration templates and patterns
Use MCPTesta's template system for different scenarios:
```bash
# Generate specialized templates
mcptesta generate-config basic ./templates/basic_connectivity.yaml \
--server-command "python simple_server.py" \
--parallel-workers 1
mcptesta generate-config advanced ./templates/full_protocol.yaml \
--server-command "python production_server.py" \
--enable-features "notifications,progress,cancellation,sampling" \
--parallel-workers 8
mcptesta generate-config stress ./templates/performance_testing.yaml \
--server-command "python performance_server.py" \
--parallel-workers 12
mcptesta generate-config integration ./templates/ci_cd_pipeline.yaml \
--server-command "docker run my-mcp-server" \
--test-types "tool_call,resource_read,prompt_get"
```
Each template provides:
- **basic**: Foundation connectivity and simple tool testing
- **intermediate**: Dependencies, error handling, and multi-suite organization
- **advanced**: Full MCP protocol features with enterprise configuration
- **expert**: Complex scenarios with advanced performance testing
- **stress**: Specialized load and performance validation
- **integration**: CI/CD pipeline patterns with environment management
## What you've mastered
In this comprehensive tutorial, you've learned:
- ✅ **YAML Architecture** - Complete understanding of config, servers, and test suite organization
- ✅ **Variable Mastery** - Advanced substitution patterns with defaults and environment integration
- ✅ **Dependency Engineering** - Complex execution flow control with parallel optimization
- ✅ **Protocol Expertise** - Full implementation of advanced MCP features
- ✅ **Enterprise Patterns** - Production-ready configurations with comprehensive error handling
- ✅ **Performance Testing** - Stress testing and load validation patterns
- ✅ **Template Systems** - Reusable configuration patterns for different complexity levels
## Advanced concepts you now understand
**Variable Inheritance**: Variables cascade through the configuration hierarchy, allowing fine-grained control at every level.
**Dependency Optimization**: MCPTesta automatically optimizes execution plans, running tests in parallel wherever dependencies allow.
**Protocol Feature Integration**: Advanced MCP features integrate seamlessly with standard testing patterns, providing enterprise-grade validation.
**Error Resilience**: Comprehensive error handling patterns ensure robust testing even in failure scenarios.
**Performance Profiling**: Built-in profiling capabilities provide detailed insights into server performance characteristics.
**Template Composition**: Configuration templates can be combined and customized for specific organizational needs.
## What's next?
With your YAML configuration mastery:
- [Master parallel testing strategies](/tutorials/parallel-testing/) - Optimize execution performance for large test suites
- [Test production servers](/how-to/test-production-servers/) - Apply enterprise patterns to real environments
- [Integrate with CI/CD](/how-to/ci-cd-integration/) - Automate comprehensive testing pipelines
- [Explore the CLI reference](/reference/cli/) - Understand all command-line options for advanced usage
- [Study the complete YAML reference](/reference/yaml/) - Deep dive into every configuration option
You're now equipped to create sophisticated, enterprise-grade test configurations that scale from simple connectivity checks to comprehensive production validation suites!

View File

@ -0,0 +1,24 @@
{
"skipLink.label": "Skip to content",
"search.label": "Search",
"search.shortcutLabel": "(Press / to Search)",
"search.cancelLabel": "Cancel",
"search.devWarning": "Search is only available in production builds. \nTry building and previewing the site to test it out locally.",
"themeSelect.accessibleLabel": "Select theme",
"themeSelect.dark": "Dark",
"themeSelect.light": "Light",
"themeSelect.auto": "Auto",
"languageSelect.accessibleLabel": "Select language",
"menuButton.accessibleLabel": "Menu",
"sidebarNav.accessibleLabel": "Main",
"tableOfContents.onThisPage": "On this page",
"tableOfContents.overview": "Overview",
"i18n.untranslatedLabel": "This page is not yet translated into {label}.",
"page.editLink": "Edit page",
"page.lastUpdated": "Last updated:",
"page.previousLink": "Previous",
"page.nextLink": "Next",
"not404.title": "Page not found",
"not404.body": "We couldn't find what you're looking for.",
"not404.linkText": "Go home"
}

1
docs/src/env.d.ts vendored Normal file
View File

@ -0,0 +1 @@
/// <reference path="../.astro/types.d.ts" />

329
docs/src/styles/custom.css Normal file
View File

@ -0,0 +1,329 @@
/* MCPTesta Documentation Custom Styles */
:root {
/* Color scheme aligned with MCPTesta branding */
--mcptesta-primary: #0ea5e9;
--mcptesta-secondary: #3b82f6;
--mcptesta-accent: #06b6d4;
--mcptesta-success: #10b981;
--mcptesta-warning: #f59e0b;
--mcptesta-error: #ef4444;
/* Semantic colors for different sections */
--tutorial-color: #10b981;
--howto-color: #3b82f6;
--reference-color: #6366f1;
--explanation-color: #8b5cf6;
/* Enhanced contrast for better readability */
--code-bg: #1e293b;
--code-text: #e2e8f0;
--border-subtle: #e5e7eb;
}
/* Diátaxis section indicators */
.tutorial-section {
border-left: 4px solid var(--tutorial-color);
padding-left: 1rem;
background: linear-gradient(to right, rgba(16, 185, 129, 0.05), transparent);
}
.howto-section {
border-left: 4px solid var(--howto-color);
padding-left: 1rem;
background: linear-gradient(to right, rgba(59, 130, 246, 0.05), transparent);
}
.reference-section {
border-left: 4px solid var(--reference-color);
padding-left: 1rem;
background: linear-gradient(to right, rgba(99, 102, 241, 0.05), transparent);
}
.explanation-section {
border-left: 4px solid var(--explanation-color);
padding-left: 1rem;
background: linear-gradient(to right, rgba(139, 92, 246, 0.05), transparent);
}
/* Enhanced code blocks */
pre {
border-radius: 8px;
border: 1px solid var(--border-subtle);
position: relative;
overflow-x: auto;
}
/* Language labels for code blocks */
pre[data-language]::before {
content: attr(data-language);
position: absolute;
top: 8px;
right: 12px;
font-size: 0.75rem;
color: #94a3b8;
text-transform: uppercase;
font-weight: 600;
letter-spacing: 0.05em;
}
/* YAML-specific styling */
pre[data-language="yaml"]::before {
content: "YAML";
color: var(--mcptesta-accent);
}
pre[data-language="bash"]::before {
content: "BASH";
color: var(--tutorial-color);
}
pre[data-language="python"]::before {
content: "PYTHON";
color: var(--howto-color);
}
/* Enhanced admonitions */
.admonition {
border-radius: 8px;
border-left: 4px solid;
padding: 1rem;
margin: 1.5rem 0;
background: rgba(255, 255, 255, 0.5);
}
.admonition.note {
border-left-color: var(--mcptesta-primary);
background: rgba(14, 165, 233, 0.05);
}
.admonition.tip {
border-left-color: var(--mcptesta-success);
background: rgba(16, 185, 129, 0.05);
}
.admonition.warning {
border-left-color: var(--mcptesta-warning);
background: rgba(245, 158, 11, 0.05);
}
.admonition.danger {
border-left-color: var(--mcptesta-error);
background: rgba(239, 68, 68, 0.05);
}
/* Navigation enhancements */
.sidebar-nav {
--section-spacing: 0.5rem;
}
/* Add icons to navigation sections */
.sidebar-nav a[href*="/tutorials/"]::before {
content: "🎓 ";
color: var(--tutorial-color);
}
.sidebar-nav a[href*="/how-to/"]::before {
content: "🔧 ";
color: var(--howto-color);
}
.sidebar-nav a[href*="/reference/"]::before {
content: "📚 ";
color: var(--reference-color);
}
.sidebar-nav a[href*="/explanation/"]::before {
content: "💡 ";
color: var(--explanation-color);
}
/* Table enhancements */
table {
border-collapse: collapse;
border-radius: 8px;
overflow: hidden;
border: 1px solid var(--border-subtle);
}
th {
background: rgba(14, 165, 233, 0.1);
font-weight: 600;
text-align: left;
padding: 12px 16px;
}
td {
padding: 12px 16px;
border-top: 1px solid var(--border-subtle);
}
/* Command line styling */
.cli-command {
background: var(--code-bg);
color: var(--code-text);
padding: 0.25rem 0.5rem;
border-radius: 4px;
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
font-size: 0.9em;
}
/* Configuration examples styling */
.config-example {
border: 2px solid var(--mcptesta-accent);
border-radius: 8px;
padding: 1rem;
background: rgba(6, 182, 212, 0.05);
}
.config-example h4 {
margin-top: 0;
color: var(--mcptesta-accent);
display: flex;
align-items: center;
gap: 0.5rem;
}
.config-example h4::before {
content: "⚙️";
}
/* Test result styling */
.test-result {
display: inline-flex;
align-items: center;
gap: 0.25rem;
padding: 0.25rem 0.5rem;
border-radius: 4px;
font-size: 0.875rem;
font-weight: 500;
}
.test-result.passed {
background: rgba(16, 185, 129, 0.1);
color: var(--mcptesta-success);
}
.test-result.passed::before {
content: "✅";
}
.test-result.failed {
background: rgba(239, 68, 68, 0.1);
color: var(--mcptesta-error);
}
.test-result.failed::before {
content: "❌";
}
.test-result.skipped {
background: rgba(245, 158, 11, 0.1);
color: var(--mcptesta-warning);
}
.test-result.skipped::before {
content: "⏭️";
}
/* Progress indicators */
.progress-indicator {
display: inline-block;
animation: spin 1s linear infinite;
}
@keyframes spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
/* Feature badges */
.feature-badge {
display: inline-flex;
align-items: center;
gap: 0.25rem;
padding: 0.25rem 0.75rem;
background: rgba(14, 165, 233, 0.1);
color: var(--mcptesta-primary);
border-radius: 20px;
font-size: 0.75rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.05em;
}
.feature-badge.advanced {
background: rgba(139, 92, 246, 0.1);
color: var(--explanation-color);
}
.feature-badge.experimental {
background: rgba(245, 158, 11, 0.1);
color: var(--mcptesta-warning);
}
/* Responsive design improvements */
@media (max-width: 768px) {
.config-example {
padding: 0.75rem;
}
pre {
font-size: 0.875rem;
}
table {
font-size: 0.875rem;
}
}
/* Print styles */
@media print {
.sidebar-nav a::before {
display: none;
}
.admonition {
border: 1px solid #333;
background: none;
}
pre {
border: 1px solid #333;
background: #f8f9fa;
}
}
/* Accessibility improvements */
@media (prefers-reduced-motion: reduce) {
.progress-indicator {
animation: none;
}
* {
transition: none !important;
}
}
/* Focus improvements */
a:focus,
button:focus {
outline: 2px solid var(--mcptesta-primary);
outline-offset: 2px;
border-radius: 4px;
}
/* High contrast mode support */
@media (prefers-contrast: high) {
:root {
--border-subtle: #000;
}
.admonition {
border-width: 2px;
}
table {
border-width: 2px;
}
}

View File

@ -0,0 +1,255 @@
# MCPTesta Configuration Templates
This directory contains comprehensive configuration templates for MCPTesta, demonstrating different complexity levels and use cases.
## Available Templates
### 📚 Basic Template (`basic_template.yaml`)
**Perfect for beginners and quick validation**
- Simple server configuration
- Basic connectivity and tool testing
- Console output with clear documentation
- Essential features only
**Use when:**
- Learning MCPTesta fundamentals
- Quick server validation
- Simple testing scenarios
### 📈 Intermediate Template (`intermediate_template.yaml`)
**Mid-level template with advanced features**
- Multiple test suites with dependencies
- Basic MCP protocol features (notifications, progress)
- HTML reporting and environment variables
- Error handling and retry policies
**Use when:**
- Need dependency management between tests
- Want to explore MCP protocol features
- Require organized test suites with setup/teardown
### 🚀 Advanced Template (`advanced_template.yaml`)
**Full-featured template with all MCP capabilities**
- Complete MCP protocol feature testing
- Multi-server coordination
- Performance testing and profiling
- Complex scenarios and error handling
**Use when:**
- Production-ready testing
- Need comprehensive MCP protocol coverage
- Testing complex multi-server scenarios
### 🎯 Expert Template (`expert_template.yaml`)
**Maximum complexity for expert users**
- Distributed system testing
- Chaos engineering patterns
- Advanced authentication schemes
- Enterprise-grade features
**Use when:**
- Expert-level protocol testing
- Distributed system validation
- Chaos engineering and resilience testing
- Protocol development and research
### ⚡ Stress Template (`stress_template.yaml`)
**Specialized performance and load testing**
- Various load patterns and stress scenarios
- Resource exhaustion testing
- Performance benchmarking
- Long-duration stability testing
**Use when:**
- Performance validation
- Load testing and capacity planning
- Identifying bottlenecks and limits
- Stability and endurance testing
### 🔗 Integration Template (`integration_template.yaml`)
**Multi-service and external system integration**
- End-to-end workflow testing
- Service mesh and discovery
- External system integration
- Transaction management
**Use when:**
- Testing multi-service architectures
- Validating service interactions
- External system integration
- Workflow orchestration testing
## Usage Examples
### Basic Usage
```bash
# Generate a basic template
mcptesta generate-config basic my_basic_config.yaml
# Generate with custom server command
mcptesta generate-config basic my_config.yaml --server-command "uvx my-server"
```
### Advanced Usage
```bash
# Generate advanced template with custom features
mcptesta generate-config advanced advanced_config.yaml \
--server-command "python -m my_server --production" \
--parallel-workers 8 \
--enable-features "notifications,progress,cancellation"
# Generate stress testing configuration
mcptesta generate-config stress stress_test.yaml \
--parallel-workers 16 \
--test-types "tool_call,resource_read"
```
### Running Generated Templates
```bash
# Run basic configuration
mcptesta yaml my_basic_config.yaml
# Run with custom settings
mcptesta yaml advanced_config.yaml --parallel 12 --output ./results
# Run specific test categories
mcptesta yaml stress_test.yaml --tag performance --tag load
```
## Template Features Comparison
| Feature | Basic | Intermediate | Advanced | Expert | Stress | Integration |
|---------|-------|--------------|----------|---------|---------|-------------|
| **Complexity** | Low | Medium | High | Very High | High | High |
| **Parallel Workers** | 2 | 4 | 6 | 12 | 16 | 8 |
| **Test Suites** | 3 | 7 | 12 | 15+ | 10 | 8 |
| **MCP Protocol Features** | Basic | Some | All | All+ | Some | All |
| **Multi-Server** | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ |
| **Dependencies** | ❌ | ✅ | ✅ | ✅ | ❌ | ✅ |
| **Performance Testing** | ❌ | ❌ | ✅ | ✅ | ✅ | ✅ |
| **External Systems** | ❌ | ❌ | ✅ | ✅ | ❌ | ✅ |
| **Chaos Engineering** | ❌ | ❌ | ❌ | ✅ | ✅ | ❌ |
| **Documentation Level** | High | High | Medium | Low | Medium | Medium |
## Customization Guide
### 1. Server Configuration
Update server commands to match your FastMCP server:
```yaml
servers:
- name: "my_server"
command: "python -m my_fastmcp_server" # Update this
transport: "stdio"
timeout: 30
```
### 2. Test Parameters
Modify test parameters to match your server's API:
```yaml
- name: "my_tool_test"
test_type: "tool_call"
target: "echo" # Change to your tool name
parameters:
message: "test" # Update parameters
```
### 3. Expected Results
Define what you expect from your server:
```yaml
expected:
message: "test" # What your server should return
status: "success"
```
### 4. Environment Variables
Use variables for flexible configuration:
```yaml
variables:
SERVER_COMMAND: "python -m my_server"
TEST_MESSAGE: "Hello, World!"
TIMEOUT: "30"
```
## Best Practices
### 1. Start Simple
- Begin with the basic template
- Gradually move to more complex templates as you learn
### 2. Incremental Complexity
- Don't jump straight to expert level
- Add features progressively based on your needs
### 3. Test Organization
- Use meaningful test names and descriptions
- Group related tests in suites
- Use tags for easy filtering
### 4. Error Handling
- Include negative test cases
- Test error conditions explicitly
- Validate error messages and types
### 5. Performance Considerations
- Monitor resource usage during testing
- Use appropriate parallel worker counts
- Include performance baselines
## Template Development
### Creating Custom Templates
For specific use cases, you can create custom templates by:
1. Starting with the closest existing template
2. Modifying test cases for your scenario
3. Adjusting configuration parameters
4. Adding custom variables and documentation
### Contributing Templates
If you develop useful templates for specific scenarios:
1. Document the use case clearly
2. Include comprehensive examples
3. Test thoroughly with different servers
4. Consider contributing back to the project
## Troubleshooting
### Common Issues
1. **Server Connection Failures**
- Check server command and transport type
- Verify server starts correctly
- Check timeout settings
2. **Test Failures**
- Verify tool/resource names match your server
- Check parameter formats and types
- Review expected results
3. **Performance Issues**
- Reduce parallel worker count
- Increase timeout values
- Check system resources
4. **Template Errors**
- Validate YAML syntax
- Check variable references
- Verify dependencies exist
### Getting Help
- Use `mcptesta yaml config.yaml --dry-run` to validate configurations
- Use `mcptesta yaml config.yaml --list-tests` to see what will run
- Check the MCPTesta documentation for detailed guidance
- Review server logs for connection and execution issues
## Template Evolution
These templates evolve based on:
- MCPTesta feature additions
- Community feedback and contributions
- Best practices discovery
- Real-world usage patterns
Stay updated with the latest templates and contribute your own improvements!

View File

@ -0,0 +1,503 @@
# MCPTesta Advanced Configuration Template
#
# This template demonstrates advanced MCPTesta capabilities including:
# - Full MCP protocol feature testing
# - Complex dependency management
# - Performance and stress testing
# - Advanced error handling and recovery
# - Comprehensive monitoring and profiling
# - Multi-server coordination
# Global configuration with all features enabled
config:
parallel_workers: 6
output_directory: "./advanced_test_results"
output_format: "all" # Generate all report types
global_timeout: 300
max_concurrent_operations: 15
# Enable all advanced features
enable_stress_testing: true
enable_memory_profiling: true
enable_performance_profiling: true
# Complete feature set
features:
test_notifications: true
test_cancellation: true
test_progress: true
test_sampling: true
test_auth: false # Enable if authentication is configured
# Advanced retry policy
retry_policy:
max_retries: 3
backoff_factor: 2.0
retry_on_errors: ["ConnectionError", "TimeoutError", "ServerError"]
exponential_backoff: true
# Comprehensive notification configuration
notifications:
enable_resource_changes: true
enable_tool_changes: true
enable_prompt_changes: true
notification_timeout: 45
buffer_size: 1000
# Multi-server configuration with load balancing
servers:
- name: "primary_server"
command: "${PRIMARY_SERVER_CMD:python -m my_fastmcp_server}"
transport: "stdio"
timeout: 30
enabled: true
env_vars:
DEBUG: "${DEBUG:1}"
LOG_LEVEL: "${LOG_LEVEL:DEBUG}"
MAX_CONNECTIONS: "100"
CACHE_SIZE: "1000"
working_directory: "${SERVER_DIR:.}"
- name: "secondary_server"
command: "${SECONDARY_SERVER_CMD:python -m my_fastmcp_server --instance 2}"
transport: "stdio"
timeout: 30
enabled: true
env_vars:
DEBUG: "${DEBUG:1}"
INSTANCE_ID: "2"
- name: "sse_server"
command: "${SSE_SERVER_URL:http://localhost:8080/sse}"
transport: "sse"
timeout: 30
enabled: false
headers:
"User-Agent": "MCPTesta-Advanced/1.0"
"Accept": "text/event-stream"
- name: "websocket_server"
command: "${WS_SERVER_URL:ws://localhost:8081/mcp}"
transport: "ws"
timeout: 30
enabled: false
# Comprehensive test suites covering all scenarios
test_suites:
- name: "Infrastructure Validation"
description: "Comprehensive server and infrastructure testing"
enabled: true
tags: ["infrastructure", "setup"]
parallel: false
timeout: 120
setup:
validate_connection: true
discover_capabilities: true
warm_up_cache: true
tests:
- name: "multi_server_connectivity"
description: "Test connectivity to all configured servers"
test_type: "ping"
target: ""
timeout: 15
tags: ["connectivity", "multi"]
- name: "capability_matrix"
description: "Build comprehensive capability matrix"
test_type: "tool_call"
target: "list_tools"
timeout: 20
tags: ["discovery"]
depends_on: ["multi_server_connectivity"]
- name: "performance_baseline"
description: "Establish performance baseline"
test_type: "tool_call"
target: "benchmark"
parameters:
operations: 100
complexity: "medium"
timeout: 60
tags: ["baseline", "performance"]
depends_on: ["capability_matrix"]
- name: "Advanced Protocol Features"
description: "Test cutting-edge MCP protocol capabilities"
enabled: true
tags: ["protocol", "advanced"]
parallel: false
timeout: 180
tests:
- name: "notification_orchestration"
description: "Complex notification subscription and handling"
test_type: "notification"
target: "all_changes"
parameters:
filter: ["resources", "tools", "prompts"]
batch_size: 50
timeout: 45
tags: ["notifications", "orchestration"]
- name: "progress_monitoring_complex"
description: "Multi-stage progress monitoring"
test_type: "tool_call"
target: "multi_stage_process"
parameters:
stages: ["initialize", "process", "validate", "finalize"]
stage_duration: 5
enable_progress: true
timeout: 30
tags: ["progress", "multi_stage"]
- name: "cancellation_recovery"
description: "Test cancellation and recovery mechanisms"
test_type: "tool_call"
target: "long_running_task"
parameters:
duration: 60
checkpoints: 5
enable_cancellation: true
enable_progress: true
timeout: 15 # Will trigger cancellation
tags: ["cancellation", "recovery"]
- name: "sampling_strategies"
description: "Test various sampling strategies"
test_type: "tool_call"
target: "echo"
parameters:
message: "Sampling test ${ITERATION}"
enable_sampling: true
sampling_rate: 0.3
retry_count: 50
timeout: 30
tags: ["sampling", "strategies"]
- name: "batch_operations"
description: "Test batch operation handling"
test_type: "tool_call"
target: "batch_process"
parameters:
batch_size: 100
operations:
- type: "transform"
data: "${BATCH_DATA}"
- type: "validate"
- type: "store"
enable_progress: true
timeout: 45
tags: ["batch", "bulk"]
- name: "Complex Tool Interactions"
description: "Sophisticated tool testing with complex scenarios"
enabled: true
tags: ["tools", "complex"]
parallel: true
timeout: 240
tests:
- name: "tool_composition"
description: "Compose multiple tools in sequence"
test_type: "tool_call"
target: "pipeline"
parameters:
steps:
- tool: "data_extract"
params: {source: "database", query: "SELECT * FROM items"}
- tool: "data_transform"
params: {format: "json", validate: true}
- tool: "data_store"
params: {destination: "cache", ttl: 3600}
enable_progress: true
timeout: 60
tags: ["composition", "pipeline"]
- name: "conditional_execution"
description: "Conditional tool execution based on results"
test_type: "tool_call"
target: "conditional_processor"
parameters:
conditions:
- if: "data_valid"
then: "process_data"
else: "handle_error"
data: "${TEST_DATA}"
timeout: 30
tags: ["conditional", "logic"]
- name: "resource_tool_integration"
description: "Integration between resources and tools"
test_type: "tool_call"
target: "process_resource"
parameters:
resource_uri: "file://./test_data.json"
processor: "json_analyzer"
output_format: "summary"
timeout: 25
tags: ["integration", "resources"]
- name: "Advanced Resource Management"
description: "Complex resource operations and management"
enabled: true
tags: ["resources", "advanced"]
parallel: true
timeout: 150
tests:
- name: "dynamic_resource_discovery"
description: "Dynamically discover and test resources"
test_type: "resource_read"
target: "discovery://auto"
parameters:
patterns: ["config://*", "file://*.json", "data://*"]
max_depth: 3
timeout: 30
tags: ["discovery", "dynamic"]
- name: "resource_streaming"
description: "Test large resource streaming"
test_type: "resource_read"
target: "stream://large_dataset"
parameters:
chunk_size: 8192
max_chunks: 1000
timeout: 45
tags: ["streaming", "large"]
- name: "resource_caching"
description: "Test resource caching mechanisms"
test_type: "resource_read"
target: "cached://expensive_computation"
parameters:
cache_ttl: 300
force_refresh: false
timeout: 60
tags: ["caching", "optimization"]
- name: "Prompt Engineering Suite"
description: "Advanced prompt testing and optimization"
enabled: true
tags: ["prompts", "engineering"]
parallel: true
timeout: 120
tests:
- name: "prompt_chain"
description: "Chain multiple prompts for complex reasoning"
test_type: "prompt_get"
target: "reasoning_chain"
parameters:
problem: "Analyze server performance bottlenecks"
steps: ["data_collection", "analysis", "recommendations"]
context: "${PERFORMANCE_DATA}"
timeout: 45
tags: ["chain", "reasoning"]
- name: "adaptive_prompts"
description: "Test adaptive prompt generation"
test_type: "prompt_get"
target: "adaptive"
parameters:
user_level: "expert"
domain: "system_administration"
task_complexity: "high"
timeout: 20
tags: ["adaptive", "personalization"]
- name: "prompt_optimization"
description: "Test prompt performance optimization"
test_type: "prompt_get"
target: "optimized"
parameters:
optimization_level: "maximum"
cache_hints: true
parallel_generation: true
enable_progress: true
timeout: 30
tags: ["optimization", "performance"]
- name: "Stress Testing Suite"
description: "Comprehensive stress testing scenarios"
enabled: true
tags: ["stress", "performance"]
parallel: true
timeout: 600
tests:
- name: "high_concurrency_test"
description: "Test high concurrency scenarios"
test_type: "tool_call"
target: "echo"
parameters:
message: "Concurrency test ${WORKER_ID}"
retry_count: 500
timeout: 120
tags: ["concurrency", "load"]
- name: "memory_pressure_test"
description: "Test under memory pressure"
test_type: "tool_call"
target: "memory_intensive"
parameters:
data_size: "50MB"
operations: 100
timeout: 180
tags: ["memory", "pressure"]
- name: "sustained_load_test"
description: "Sustained load over extended period"
test_type: "tool_call"
target: "sustained_operation"
parameters:
duration: 300 # 5 minutes
rate: 10 # 10 operations per second
enable_progress: true
timeout: 360
tags: ["sustained", "endurance"]
- name: "failure_recovery_test"
description: "Test recovery from various failure modes"
test_type: "tool_call"
target: "failure_simulator"
parameters:
failure_modes: ["connection_drop", "timeout", "memory_error"]
recovery_strategy: "exponential_backoff"
retry_count: 10
timeout: 120
tags: ["failure", "recovery"]
- name: "Edge Cases and Boundaries"
description: "Test edge cases and boundary conditions"
enabled: true
tags: ["edge_cases", "boundaries"]
parallel: true
timeout: 180
tests:
- name: "maximum_payload_test"
description: "Test maximum payload handling"
test_type: "tool_call"
target: "echo"
parameters:
large_data: "${GENERATE_LARGE_PAYLOAD:10MB}"
timeout: 60
tags: ["payload", "limits"]
- name: "unicode_handling"
description: "Test Unicode and special character handling"
test_type: "tool_call"
target: "echo"
parameters:
message: "Testing 🧪 Unicode: 测试 العربية русский 日本語"
encoding: "utf-8"
timeout: 15
tags: ["unicode", "encoding"]
- name: "nested_data_structures"
description: "Test deeply nested data structures"
test_type: "tool_call"
target: "deep_processor"
parameters:
data:
level1:
level2:
level3:
level4:
level5:
deep_value: "Found at level 5"
array: [1, 2, 3, [4, 5, [6, 7, 8]]]
timeout: 20
tags: ["nested", "structures"]
- name: "Security and Validation"
description: "Security testing and input validation"
enabled: true
tags: ["security", "validation"]
parallel: true
timeout: 120
tests:
- name: "input_sanitization"
description: "Test input sanitization"
test_type: "tool_call"
target: "sanitize_input"
parameters:
potentially_malicious: "<script>alert('xss')</script>"
sql_injection: "'; DROP TABLE users; --"
expected:
sanitized: true
threats_detected: 2
timeout: 15
tags: ["sanitization", "xss"]
- name: "rate_limiting"
description: "Test rate limiting mechanisms"
test_type: "tool_call"
target: "echo"
parameters:
message: "Rate limit test"
retry_count: 1000 # Should trigger rate limiting
timeout: 60
expected_error: "rate limit exceeded"
tags: ["rate_limiting", "throttling"]
- name: "authentication_validation"
description: "Test authentication mechanisms"
test_type: "tool_call"
target: "protected_resource"
parameters:
auth_token: "${INVALID_TOKEN:invalid_token_123}"
expected_error: "authentication failed"
timeout: 10
tags: ["auth", "security"]
# Comprehensive variables for advanced configuration
variables:
PRIMARY_SERVER_CMD: "python -m my_fastmcp_server --advanced"
SECONDARY_SERVER_CMD: "python -m my_fastmcp_server --replica"
SSE_SERVER_URL: "http://localhost:8080/sse"
WS_SERVER_URL: "ws://localhost:8081/mcp"
DEBUG: "1"
LOG_LEVEL: "DEBUG"
SERVER_DIR: "/path/to/server"
TEST_DATA: '{"items": [1,2,3,4,5], "metadata": {"source": "test"}}'
PERFORMANCE_DATA: '{"cpu": 45, "memory": 2048, "latency": 12.5}'
BATCH_DATA: '[{"id": 1, "value": "test1"}, {"id": 2, "value": "test2"}]'
GENERATE_LARGE_PAYLOAD: "generate_data_mb"
INVALID_TOKEN: "deliberately_invalid_token_for_testing"
ITERATION: "0"
WORKER_ID: "worker_1"
# Advanced Usage Notes:
#
# Performance Monitoring:
# - Enable profiling: enable_memory_profiling, enable_performance_profiling
# - Use HTML reports for detailed visualization
# - Monitor resource usage during stress tests
#
# Parallel Execution:
# - Dependency management ensures correct execution order
# - Use parallel: false for tests that must run sequentially
# - Balance parallel_workers with system resources
#
# Error Handling:
# - expected_error tests validate error conditions
# - Retry policies handle transient failures
# - Cancellation tests verify graceful shutdown
#
# Customization:
# - Variables support environment-specific values
# - Tags enable selective test execution
# - Conditional execution based on server capabilities
#
# Run Examples:
# mcptesta yaml advanced_config.yaml --parallel 8 --output ./results
# mcptesta yaml advanced_config.yaml --tag performance --format html
# mcptesta yaml advanced_config.yaml --exclude-tag stress --dry-run

View File

@ -0,0 +1,131 @@
# MCPTesta Basic Configuration Template
#
# This template provides a simple starting point for testing FastMCP servers.
# Perfect for beginners or quick validation testing.
#
# Features demonstrated:
# - Single server testing
# - Basic tool and resource testing
# - Simple parallel execution
# - Console output
# Global configuration
config:
# Number of parallel test workers (1-8 recommended for basic testing)
parallel_workers: 2
# Output format: console, html, json, junit
output_format: "console"
# Global timeout for all operations (seconds)
global_timeout: 120
# Maximum concurrent operations per worker
max_concurrent_operations: 5
# Server configuration
servers:
- name: "my_server"
# Command to start your FastMCP server
# Examples:
# "python -m my_fastmcp_server"
# "uvx my-mcp-server"
# "node server.js"
command: "python -m my_fastmcp_server"
# Transport protocol: stdio (most common), sse, ws
transport: "stdio"
# Connection timeout in seconds
timeout: 30
# Enable this server for testing
enabled: true
# Test suites - organized groups of related tests
test_suites:
- name: "Basic Connectivity"
description: "Verify server is responding and accessible"
enabled: true
tags: ["connectivity", "basic"]
parallel: true
timeout: 60
tests:
- name: "ping_test"
description: "Basic connectivity check"
test_type: "ping"
target: ""
timeout: 10
tags: ["ping"]
- name: "capabilities_discovery"
description: "Discover server capabilities"
test_type: "tool_call"
target: "list_tools" # Replace with your server's capability discovery method
timeout: 15
tags: ["discovery"]
- name: "Tool Testing"
description: "Test available tools with various parameters"
enabled: true
tags: ["tools"]
parallel: true
timeout: 90
tests:
- name: "simple_tool_test"
description: "Test a simple tool call"
test_type: "tool_call"
target: "echo" # Replace with an actual tool from your server
parameters:
message: "Hello from MCPTesta!"
expected:
# Define what you expect in the response
message: "Hello from MCPTesta!"
timeout: 15
tags: ["echo", "simple"]
# Add more tool tests here
# - name: "another_tool_test"
# description: "Test another tool"
# test_type: "tool_call"
# target: "my_other_tool"
# parameters:
# param1: "value1"
# timeout: 20
- name: "Resource Testing"
description: "Test resource reading capabilities"
enabled: true
tags: ["resources"]
parallel: true
timeout: 60
tests:
- name: "read_basic_resource"
description: "Read a basic resource"
test_type: "resource_read"
target: "file://README.md" # Replace with actual resource URI
timeout: 15
tags: ["file"]
# Add more resource tests here
# - name: "read_config_resource"
# test_type: "resource_read"
# target: "config://settings.json"
# Variables for easy customization
variables:
SERVER_NAME: "my_server"
TEST_MESSAGE: "Hello from MCPTesta!"
DEFAULT_TIMEOUT: "30"
# Quick Start Instructions:
# 1. Replace "python -m my_fastmcp_server" with your actual server command
# 2. Update tool names (like "echo") with tools your server provides
# 3. Modify resource URIs to match your server's resources
# 4. Run with: mcptesta yaml this_config.yaml
#
# For more advanced features, generate an "intermediate" or "advanced" template:
# mcptesta generate-config intermediate my_advanced_config.yaml

View File

@ -0,0 +1,625 @@
# MCPTesta Expert Configuration Template
#
# This is the most comprehensive template demonstrating every MCPTesta capability.
# Designed for expert users who need maximum control and testing depth.
#
# Expert Features:
# - Multi-dimensional test matrices
# - Dynamic test generation
# - Advanced authentication schemes
# - Custom protocol extensions
# - Real-time monitoring and alerting
# - Distributed testing coordination
# - Performance regression detection
# - Chaos engineering patterns
# Expert-level global configuration
config:
parallel_workers: 12
output_directory: "./expert_test_results"
output_format: "all"
global_timeout: 900 # 15 minutes for complex scenarios
max_concurrent_operations: 50
# All features enabled with advanced settings
enable_stress_testing: true
enable_memory_profiling: true
enable_performance_profiling: true
enable_chaos_testing: true
enable_regression_detection: true
# Expert feature configuration
features:
test_notifications: true
test_cancellation: true
test_progress: true
test_sampling: true
test_auth: true
test_custom_protocols: true
test_distributed_coordination: true
# Advanced retry and circuit breaker configuration
retry_policy:
max_retries: 5
backoff_factor: 2.5
retry_on_errors: ["ConnectionError", "TimeoutError", "ServerError", "AuthError"]
exponential_backoff: true
jitter: true
circuit_breaker:
failure_threshold: 10
recovery_timeout: 30
half_open_max_calls: 3
# Comprehensive notification system
notifications:
enable_resource_changes: true
enable_tool_changes: true
enable_prompt_changes: true
enable_server_metrics: true
enable_performance_alerts: true
notification_timeout: 60
buffer_size: 10000
batch_processing: true
# Performance monitoring and alerting
monitoring:
enable_real_time_metrics: true
metrics_collection_interval: 1
performance_thresholds:
max_latency_ms: 1000
max_memory_mb: 512
max_cpu_percent: 80
alert_on_threshold_breach: true
# Chaos engineering configuration
chaos_testing:
enabled: true
failure_injection_rate: 0.05
failure_types: ["network_delay", "memory_pressure", "cpu_spike"]
recovery_validation: true
# Multi-environment server matrix
servers:
# Production-like environments
- name: "production_primary"
command: "${PROD_PRIMARY_CMD:python -m my_fastmcp_server --env prod --instance primary}"
transport: "stdio"
timeout: 45
enabled: true
env_vars:
ENV: "production"
INSTANCE_TYPE: "primary"
MAX_CONNECTIONS: "1000"
CACHE_SIZE: "10000"
ENABLE_METRICS: "true"
auth_token: "${PROD_AUTH_TOKEN}"
auth_type: "bearer"
- name: "production_secondary"
command: "${PROD_SECONDARY_CMD:python -m my_fastmcp_server --env prod --instance secondary}"
transport: "stdio"
timeout: 45
enabled: true
env_vars:
ENV: "production"
INSTANCE_TYPE: "secondary"
auth_token: "${PROD_AUTH_TOKEN}"
# Staging environment
- name: "staging_server"
command: "${STAGING_CMD:python -m my_fastmcp_server --env staging}"
transport: "sse"
timeout: 30
enabled: true
headers:
"Authorization": "Bearer ${STAGING_TOKEN}"
"Environment": "staging"
# Development environments with various transports
- name: "dev_stdio"
command: "${DEV_STDIO_CMD:python -m my_fastmcp_server --env dev --debug}"
transport: "stdio"
timeout: 20
enabled: true
- name: "dev_websocket"
command: "${DEV_WS_URL:ws://localhost:8081/mcp}"
transport: "ws"
timeout: 30
enabled: true
# Performance testing dedicated servers
- name: "perf_server_1"
command: "${PERF_CMD:python -m my_fastmcp_server --performance-mode}"
transport: "stdio"
timeout: 60
enabled: true
env_vars:
PERFORMANCE_MODE: "true"
GC_OPTIMIZATION: "true"
- name: "perf_server_2"
command: "${PERF_CMD:python -m my_fastmcp_server --performance-mode --instance 2}"
transport: "stdio"
timeout: 60
enabled: true
# Expert-level test suites with comprehensive coverage
test_suites:
- name: "Environment Matrix Validation"
description: "Validate functionality across all environments and configurations"
enabled: true
tags: ["matrix", "validation", "environments"]
parallel: true
timeout: 300
setup:
validate_all_connections: true
establish_baseline_metrics: true
configure_monitoring: true
teardown:
generate_comparison_report: true
archive_metrics: true
tests:
- name: "cross_environment_consistency"
description: "Ensure consistent behavior across environments"
test_type: "tool_call"
target: "consistency_check"
parameters:
environments: ["production", "staging", "development"]
validation_suite: "comprehensive"
timeout: 60
tags: ["consistency", "cross_env"]
- name: "performance_parity"
description: "Validate performance parity between instances"
test_type: "tool_call"
target: "benchmark"
parameters:
test_suite: "standard"
iterations: 1000
measure: ["latency", "throughput", "resource_usage"]
enable_progress: true
timeout: 120
tags: ["performance", "parity"]
- name: "Protocol Compliance and Extensions"
description: "Comprehensive MCP protocol compliance and custom extensions"
enabled: true
tags: ["protocol", "compliance", "extensions"]
parallel: false
timeout: 400
tests:
- name: "mcp_specification_compliance"
description: "Full MCP specification compliance testing"
test_type: "tool_call"
target: "protocol_validator"
parameters:
specification_version: "latest"
test_categories: ["transport", "messages", "capabilities", "errors"]
strict_mode: true
timeout: 90
tags: ["compliance", "specification"]
- name: "custom_protocol_extensions"
description: "Test custom protocol extensions"
test_type: "tool_call"
target: "extension_handler"
parameters:
extensions: ["streaming", "batch_operations", "custom_auth"]
compatibility_mode: false
timeout: 45
tags: ["extensions", "custom"]
- name: "protocol_version_negotiation"
description: "Test protocol version negotiation"
test_type: "tool_call"
target: "version_negotiator"
parameters:
supported_versions: ["1.0", "1.1", "2.0-draft"]
preferred_version: "1.1"
timeout: 20
tags: ["negotiation", "versions"]
- name: "Advanced Authentication and Authorization"
description: "Comprehensive authentication and authorization testing"
enabled: true
tags: ["auth", "security", "advanced"]
parallel: true
timeout: 200
tests:
- name: "oauth2_flow_complete"
description: "Complete OAuth2 authentication flow"
test_type: "tool_call"
target: "oauth2_authenticator"
parameters:
grant_type: "authorization_code"
client_id: "${OAUTH_CLIENT_ID}"
client_secret: "${OAUTH_CLIENT_SECRET}"
scope: "mcp:full_access"
timeout: 45
tags: ["oauth2", "flow"]
- name: "token_refresh_mechanism"
description: "Test token refresh and renewal"
test_type: "tool_call"
target: "token_manager"
parameters:
initial_token: "${SHORT_LIVED_TOKEN}"
refresh_token: "${REFRESH_TOKEN}"
auto_refresh: true
timeout: 30
tags: ["token", "refresh"]
- name: "role_based_access_control"
description: "Test role-based access control"
test_type: "tool_call"
target: "rbac_validator"
parameters:
user_roles: ["admin", "user", "readonly"]
resource_permissions: ["read", "write", "execute"]
test_matrix: true
timeout: 60
tags: ["rbac", "permissions"]
- name: "jwt_validation_comprehensive"
description: "Comprehensive JWT validation testing"
test_type: "tool_call"
target: "jwt_validator"
parameters:
test_cases: ["valid", "expired", "invalid_signature", "malformed"]
algorithms: ["HS256", "RS256", "ES256"]
timeout: 40
tags: ["jwt", "validation"]
- name: "Distributed System Coordination"
description: "Test distributed system patterns and coordination"
enabled: true
tags: ["distributed", "coordination", "scaling"]
parallel: false
timeout: 500
tests:
- name: "leader_election"
description: "Test leader election mechanisms"
test_type: "tool_call"
target: "leader_elector"
parameters:
nodes: ["node1", "node2", "node3"]
election_timeout: 30
heartbeat_interval: 5
enable_progress: true
timeout: 90
tags: ["leader", "election"]
- name: "consensus_protocol"
description: "Test consensus protocol implementation"
test_type: "tool_call"
target: "consensus_manager"
parameters:
consensus_type: "raft"
cluster_size: 5
failure_scenarios: ["network_partition", "node_failure"]
timeout: 120
tags: ["consensus", "raft"]
- name: "distributed_transaction"
description: "Test distributed transaction coordination"
test_type: "tool_call"
target: "transaction_coordinator"
parameters:
transaction_type: "two_phase_commit"
participants: ["db1", "db2", "cache"]
isolation_level: "serializable"
timeout: 80
tags: ["transaction", "2pc"]
- name: "service_mesh_integration"
description: "Test service mesh integration patterns"
test_type: "tool_call"
target: "mesh_coordinator"
parameters:
mesh_type: "istio"
features: ["load_balancing", "circuit_breaking", "observability"]
timeout: 60
tags: ["mesh", "integration"]
- name: "Chaos Engineering and Resilience"
description: "Comprehensive chaos engineering and resilience testing"
enabled: true
tags: ["chaos", "resilience", "reliability"]
parallel: true
timeout: 600
tests:
- name: "network_chaos"
description: "Network-level chaos injection"
test_type: "tool_call"
target: "chaos_injector"
parameters:
chaos_type: "network"
scenarios: ["latency_spike", "packet_loss", "connection_drop"]
intensity: "moderate"
duration: 60
enable_progress: true
timeout: 120
tags: ["network", "chaos"]
- name: "resource_exhaustion"
description: "Resource exhaustion resilience testing"
test_type: "tool_call"
target: "resource_exhaustor"
parameters:
resources: ["memory", "cpu", "disk_io", "file_descriptors"]
exhaustion_rate: "gradual"
recovery_monitoring: true
timeout: 180
tags: ["resources", "exhaustion"]
- name: "cascading_failure_simulation"
description: "Simulate and test cascading failure scenarios"
test_type: "tool_call"
target: "failure_simulator"
parameters:
initial_failure: "primary_database"
cascade_pattern: "dependency_graph"
mitigation_strategies: ["circuit_breakers", "bulkheads", "timeouts"]
timeout: 200
tags: ["cascading", "failures"]
- name: "disaster_recovery_drill"
description: "Complete disaster recovery testing"
test_type: "tool_call"
target: "disaster_recovery"
parameters:
disaster_type: "complete_datacenter_failure"
recovery_objectives: {"rto": 300, "rpo": 60}
validation_suite: "comprehensive"
timeout: 400
tags: ["disaster", "recovery"]
- name: "Performance Engineering and Optimization"
description: "Advanced performance testing and optimization validation"
enabled: true
tags: ["performance", "optimization", "engineering"]
parallel: true
timeout: 800
tests:
- name: "load_curve_analysis"
description: "Comprehensive load curve analysis"
test_type: "tool_call"
target: "load_tester"
parameters:
load_pattern: "stepped_increase"
start_rps: 1
max_rps: 10000
step_duration: 60
metrics: ["latency_percentiles", "throughput", "error_rate"]
enable_progress: true
timeout: 600
tags: ["load", "curve"]
- name: "memory_profile_analysis"
description: "Detailed memory profiling and leak detection"
test_type: "tool_call"
target: "memory_profiler"
parameters:
profile_duration: 300
heap_snapshots: true
gc_analysis: true
leak_detection: true
timeout: 360
tags: ["memory", "profiling"]
- name: "cpu_optimization_validation"
description: "CPU optimization and hot path analysis"
test_type: "tool_call"
target: "cpu_profiler"
parameters:
profiling_type: "statistical"
sampling_rate: 1000
flame_graph: true
optimization_suggestions: true
timeout: 240
tags: ["cpu", "optimization"]
- name: "database_performance_tuning"
description: "Database performance analysis and tuning"
test_type: "tool_call"
target: "db_performance_analyzer"
parameters:
databases: ["primary", "replica", "cache"]
analysis_type: "comprehensive"
query_optimization: true
index_analysis: true
timeout: 180
tags: ["database", "tuning"]
- name: "Advanced Data Scenarios"
description: "Complex data processing and validation scenarios"
enabled: true
tags: ["data", "complex", "scenarios"]
parallel: true
timeout: 400
tests:
- name: "large_dataset_processing"
description: "Process and validate large datasets"
test_type: "tool_call"
target: "dataset_processor"
parameters:
dataset_size: "1GB"
processing_type: "streaming"
validation_rules: ["schema", "data_quality", "completeness"]
output_format: "parquet"
enable_progress: true
timeout: 300
tags: ["large", "datasets"]
- name: "real_time_stream_processing"
description: "Real-time stream processing validation"
test_type: "tool_call"
target: "stream_processor"
parameters:
stream_type: "kafka"
processing_topology: "complex"
window_types: ["tumbling", "sliding", "session"]
state_management: "distributed"
timeout: 200
tags: ["streaming", "realtime"]
- name: "ml_pipeline_validation"
description: "Machine learning pipeline testing"
test_type: "tool_call"
target: "ml_pipeline"
parameters:
pipeline_stages: ["preprocessing", "training", "validation", "deployment"]
model_types: ["classification", "regression", "clustering"]
validation_metrics: ["accuracy", "precision", "recall", "f1"]
enable_progress: true
timeout: 600
tags: ["ml", "pipeline"]
- name: "Integration and Contract Testing"
description: "Advanced integration and contract testing"
enabled: true
tags: ["integration", "contracts", "apis"]
parallel: true
timeout: 300
tests:
- name: "api_contract_validation"
description: "Validate API contracts across versions"
test_type: "tool_call"
target: "contract_validator"
parameters:
contract_formats: ["openapi", "graphql", "protobuf"]
version_compatibility: ["backward", "forward"]
breaking_change_detection: true
timeout: 60
tags: ["contracts", "apis"]
- name: "event_sourcing_validation"
description: "Event sourcing pattern validation"
test_type: "tool_call"
target: "event_sourcing_validator"
parameters:
event_store: "distributed"
projections: ["read_models", "aggregates"]
consistency_models: ["eventual", "strong"]
timeout: 90
tags: ["events", "sourcing"]
- name: "microservices_choreography"
description: "Microservices choreography testing"
test_type: "tool_call"
target: "choreography_tester"
parameters:
services: ["user", "order", "payment", "inventory"]
business_processes: ["order_fulfillment", "payment_processing"]
failure_scenarios: ["service_timeout", "partial_failure"]
timeout: 120
tags: ["microservices", "choreography"]
# Expert-level variables with comprehensive configuration
variables:
# Environment commands
PROD_PRIMARY_CMD: "python -m my_fastmcp_server --env prod --instance primary --port 8080"
PROD_SECONDARY_CMD: "python -m my_fastmcp_server --env prod --instance secondary --port 8081"
STAGING_CMD: "python -m my_fastmcp_server --env staging --port 8082"
DEV_STDIO_CMD: "python -m my_fastmcp_server --env dev --debug --port 8083"
DEV_WS_URL: "ws://localhost:8084/mcp"
PERF_CMD: "python -m my_fastmcp_server --performance-mode --port 8085"
# Authentication tokens
PROD_AUTH_TOKEN: "${PROD_TOKEN}"
STAGING_TOKEN: "${STAGING_TOKEN}"
OAUTH_CLIENT_ID: "${OAUTH_CLIENT_ID}"
OAUTH_CLIENT_SECRET: "${OAUTH_CLIENT_SECRET}"
SHORT_LIVED_TOKEN: "${SHORT_TOKEN}"
REFRESH_TOKEN: "${REFRESH_TOKEN}"
# Performance and testing parameters
MAX_LOAD_RPS: "10000"
DATASET_SIZE_GB: "1"
STRESS_TEST_DURATION: "300"
CHAOS_INTENSITY: "moderate"
# Distributed system configuration
CLUSTER_SIZE: "5"
CONSENSUS_TYPE: "raft"
MESH_TYPE: "istio"
# Database and storage
PRIMARY_DB: "postgresql://prod-primary/mcptest"
REPLICA_DB: "postgresql://prod-replica/mcptest"
CACHE_URL: "redis://cache-cluster:6379"
# Monitoring and observability
METRICS_ENDPOINT: "http://prometheus:9090"
TRACING_ENDPOINT: "http://jaeger:14268"
LOG_AGGREGATOR: "http://elasticsearch:9200"
# Expert Usage Patterns and Best Practices:
#
# 1. Environment Matrix Testing:
# - Test across production, staging, and development
# - Validate configuration consistency
# - Performance parity verification
#
# 2. Advanced Protocol Testing:
# - Full MCP specification compliance
# - Custom protocol extensions
# - Version negotiation validation
#
# 3. Security and Authentication:
# - Multiple authentication mechanisms
# - Authorization matrix testing
# - Security vulnerability scanning
#
# 4. Distributed System Validation:
# - Leader election and consensus
# - Distributed transaction coordination
# - Service mesh integration
#
# 5. Chaos Engineering:
# - Network-level chaos injection
# - Resource exhaustion testing
# - Disaster recovery validation
#
# 6. Performance Engineering:
# - Load curve analysis
# - Memory and CPU profiling
# - Database optimization validation
#
# 7. Advanced Data Processing:
# - Large dataset handling
# - Real-time stream processing
# - ML pipeline validation
#
# 8. Integration Testing:
# - API contract validation
# - Event sourcing patterns
# - Microservices choreography
#
# Execution Examples:
#
# Full expert test suite:
# mcptesta yaml expert_config.yaml --parallel 12 --output ./expert_results
#
# Security-focused testing:
# mcptesta yaml expert_config.yaml --tag security --tag auth --format html
#
# Performance regression detection:
# mcptesta yaml expert_config.yaml --tag performance --enable-regression-detection
#
# Chaos engineering validation:
# mcptesta yaml expert_config.yaml --tag chaos --tag resilience --parallel 6
#
# Distributed system testing:
# mcptesta yaml expert_config.yaml --tag distributed --tag coordination --timeout 900

View File

@ -0,0 +1,610 @@
# MCPTesta Integration Testing Configuration Template
#
# Comprehensive integration testing template for multi-service environments.
# Tests real-world scenarios with multiple FastMCP servers, external systems,
# and complex workflow orchestration.
#
# Integration Testing Scenarios:
# - Multi-service coordination and communication
# - External system integration (databases, APIs, message queues)
# - End-to-end workflow validation
# - Cross-service transaction management
# - Service mesh and discovery integration
# - Event-driven architecture validation
# Integration testing optimized configuration
config:
parallel_workers: 8
output_directory: "./integration_test_results"
output_format: "html" # Rich visualization for complex scenarios
global_timeout: 600 # 10 minutes for complex integration scenarios
max_concurrent_operations: 25
# Integration-specific features
enable_distributed_tracing: true
enable_transaction_monitoring: true
enable_service_discovery: true
features:
test_notifications: true
test_progress: true
test_cancellation: true
test_auth: true
test_distributed_coordination: true
# Integration-friendly retry policy
retry_policy:
max_retries: 3
backoff_factor: 2.0
retry_on_errors: ["ConnectionError", "TimeoutError", "ServiceUnavailable"]
circuit_breaker:
failure_threshold: 5
recovery_timeout: 30
# Service discovery and coordination
service_discovery:
provider: "consul" # consul, etcd, kubernetes
health_check_interval: 10
service_registration: true
# Distributed tracing configuration
tracing:
enabled: true
sampler: "probabilistic"
sampling_rate: 1.0 # 100% sampling for integration tests
exporter: "jaeger"
# Multi-service environment setup
servers:
# Core business services
- name: "user_service"
command: "${USER_SERVICE_CMD:python -m user_service --port 8001}"
transport: "sse"
timeout: 30
enabled: true
env_vars:
SERVICE_NAME: "user_service"
DATABASE_URL: "${USER_DB_URL:postgresql://localhost/users}"
CACHE_URL: "${CACHE_URL:redis://localhost:6379/0}"
headers:
"Service-Version": "1.0"
"Environment": "${ENVIRONMENT:integration}"
- name: "order_service"
command: "${ORDER_SERVICE_CMD:python -m order_service --port 8002}"
transport: "sse"
timeout: 30
enabled: true
env_vars:
SERVICE_NAME: "order_service"
DATABASE_URL: "${ORDER_DB_URL:postgresql://localhost/orders}"
MESSAGE_QUEUE_URL: "${MQ_URL:amqp://localhost:5672}"
depends_on: ["user_service"]
- name: "payment_service"
command: "${PAYMENT_SERVICE_CMD:python -m payment_service --port 8003}"
transport: "sse"
timeout: 45 # Longer timeout for payment processing
enabled: true
env_vars:
SERVICE_NAME: "payment_service"
PAYMENT_GATEWAY_URL: "${PAYMENT_GATEWAY:https://api.stripe.com}"
ENCRYPTION_KEY: "${PAYMENT_ENCRYPTION_KEY}"
auth_token: "${PAYMENT_SERVICE_TOKEN}"
- name: "inventory_service"
command: "${INVENTORY_SERVICE_CMD:python -m inventory_service --port 8004}"
transport: "sse"
timeout: 30
enabled: true
env_vars:
SERVICE_NAME: "inventory_service"
DATABASE_URL: "${INVENTORY_DB_URL:postgresql://localhost/inventory}"
WAREHOUSE_API_URL: "${WAREHOUSE_API:http://localhost:9001}"
- name: "notification_service"
command: "${NOTIFICATION_SERVICE_CMD:python -m notification_service --port 8005}"
transport: "ws" # WebSocket for real-time notifications
timeout: 30
enabled: true
env_vars:
SERVICE_NAME: "notification_service"
EMAIL_PROVIDER: "${EMAIL_PROVIDER:sendgrid}"
SMS_PROVIDER: "${SMS_PROVIDER:twilio}"
# External system adapters
- name: "database_adapter"
command: "${DB_ADAPTER_CMD:python -m database_adapter --port 8006}"
transport: "stdio"
timeout: 30
enabled: true
env_vars:
SUPPORTED_DBS: "postgresql,mysql,mongodb"
CONNECTION_POOL_SIZE: "20"
- name: "message_queue_adapter"
command: "${MQ_ADAPTER_CMD:python -m mq_adapter --port 8007}"
transport: "stdio"
timeout: 30
enabled: true
env_vars:
SUPPORTED_QUEUES: "rabbitmq,kafka,sqs"
BATCH_SIZE: "100"
# Comprehensive integration test suites
test_suites:
- name: "Service Connectivity Matrix"
description: "Validate connectivity between all services"
enabled: true
tags: ["connectivity", "matrix", "health"]
parallel: false # Sequential for proper dependency validation
timeout: 180
setup:
wait_for_service_startup: 30
validate_service_registration: true
establish_baseline_health: true
tests:
- name: "service_discovery_validation"
description: "Validate all services are discoverable"
test_type: "tool_call"
target: "discover_services"
parameters:
expected_services: ["user", "order", "payment", "inventory", "notification"]
health_check: true
timeout: 30
tags: ["discovery", "health"]
- name: "inter_service_communication"
description: "Test communication between all service pairs"
test_type: "tool_call"
target: "test_service_matrix"
parameters:
services: ["user_service", "order_service", "payment_service"]
test_type: "ping"
timeout: 60
tags: ["communication", "matrix"]
depends_on: ["service_discovery_validation"]
- name: "service_dependency_validation"
description: "Validate service dependency chains"
test_type: "tool_call"
target: "validate_dependencies"
parameters:
dependency_graph: {
"order_service": ["user_service", "inventory_service"],
"payment_service": ["order_service", "user_service"],
"notification_service": ["order_service", "payment_service"]
}
timeout: 45
tags: ["dependencies", "validation"]
- name: "End-to-End Business Workflows"
description: "Complete business workflow integration testing"
enabled: true
tags: ["e2e", "workflows", "business"]
parallel: false # Sequential for workflow integrity
timeout: 400
tests:
- name: "user_registration_workflow"
description: "Complete user registration process"
test_type: "tool_call"
target: "user_registration"
parameters:
user_data: {
"email": "integration.test@example.com",
"name": "Integration Test User",
"phone": "+1234567890"
}
send_welcome_email: true
create_profile: true
enable_progress: true
timeout: 60
tags: ["user", "registration"]
- name: "order_placement_workflow"
description: "Complete order placement and processing"
test_type: "tool_call"
target: "place_order"
parameters:
user_id: "${USER_ID_FROM_REGISTRATION}"
items: [
{"product_id": "PROD_001", "quantity": 2},
{"product_id": "PROD_002", "quantity": 1}
]
payment_method: "credit_card"
shipping_address: {
"street": "123 Test Street",
"city": "Test City",
"zip": "12345"
}
enable_progress: true
timeout: 120
tags: ["order", "placement"]
depends_on: ["user_registration_workflow"]
- name: "payment_processing_workflow"
description: "Payment processing and validation"
test_type: "tool_call"
target: "process_payment"
parameters:
order_id: "${ORDER_ID_FROM_PLACEMENT}"
payment_details: {
"method": "credit_card",
"amount": "${ORDER_TOTAL}",
"currency": "USD"
}
fraud_check: true
enable_progress: true
timeout: 90
tags: ["payment", "processing"]
depends_on: ["order_placement_workflow"]
- name: "inventory_update_workflow"
description: "Inventory updates and stock management"
test_type: "tool_call"
target: "update_inventory"
parameters:
order_id: "${ORDER_ID_FROM_PLACEMENT}"
reservation_type: "confirmed"
update_warehouse: true
timeout: 45
tags: ["inventory", "update"]
depends_on: ["payment_processing_workflow"]
- name: "notification_workflow"
description: "Multi-channel notification delivery"
test_type: "tool_call"
target: "send_notifications"
parameters:
user_id: "${USER_ID_FROM_REGISTRATION}"
order_id: "${ORDER_ID_FROM_PLACEMENT}"
notification_types: ["email", "sms", "push"]
templates: ["order_confirmation", "payment_receipt"]
timeout: 60
tags: ["notifications", "delivery"]
depends_on: ["inventory_update_workflow"]
- name: "Cross-Service Transaction Testing"
description: "Distributed transaction management and consistency"
enabled: true
tags: ["transactions", "consistency", "distributed"]
parallel: false
timeout: 300
tests:
- name: "two_phase_commit_test"
description: "Test two-phase commit across services"
test_type: "tool_call"
target: "distributed_transaction"
parameters:
transaction_type: "2pc"
participants: ["user_service", "order_service", "payment_service"]
operations: [
{"service": "user_service", "action": "reserve_credit"},
{"service": "order_service", "action": "create_order"},
{"service": "payment_service", "action": "charge_card"}
]
enable_progress: true
timeout: 120
tags: ["2pc", "distributed"]
- name: "saga_pattern_test"
description: "Test saga pattern for long-running transactions"
test_type: "tool_call"
target: "saga_coordinator"
parameters:
saga_definition: {
"steps": [
{"service": "inventory", "action": "reserve", "compensate": "release"},
{"service": "payment", "action": "charge", "compensate": "refund"},
{"service": "shipping", "action": "create_label", "compensate": "cancel"}
]
}
compensation_strategy: "reverse_order"
enable_progress: true
timeout: 180
tags: ["saga", "compensation"]
- name: "eventual_consistency_test"
description: "Test eventual consistency patterns"
test_type: "tool_call"
target: "consistency_validator"
parameters:
consistency_model: "eventual"
propagation_timeout: 30
validation_points: ["immediate", "5s", "15s", "30s"]
timeout: 60
tags: ["consistency", "eventual"]
- name: "Event-Driven Architecture Testing"
description: "Event sourcing and message-driven integration"
enabled: true
tags: ["events", "messaging", "async"]
parallel: true
timeout: 250
tests:
- name: "event_publication_test"
description: "Test event publication and routing"
test_type: "tool_call"
target: "event_publisher"
parameters:
events: [
{"type": "UserRegistered", "data": {"user_id": "123"}},
{"type": "OrderPlaced", "data": {"order_id": "456"}},
{"type": "PaymentProcessed", "data": {"payment_id": "789"}}
]
routing_keys: ["user.registered", "order.placed", "payment.processed"]
timeout: 30
tags: ["events", "publication"]
- name: "event_subscription_test"
description: "Test event subscription and handling"
test_type: "notification"
target: "event_subscription"
parameters:
event_types: ["UserRegistered", "OrderPlaced", "PaymentProcessed"]
subscription_durability: "persistent"
timeout: 60
tags: ["events", "subscription"]
- name: "event_sourcing_replay_test"
description: "Test event sourcing and replay capabilities"
test_type: "tool_call"
target: "event_sourcing"
parameters:
aggregate_type: "Order"
event_sequence: [
{"type": "OrderCreated", "timestamp": "2024-01-01T00:00:00Z"},
{"type": "ItemAdded", "timestamp": "2024-01-01T00:01:00Z"},
{"type": "PaymentProcessed", "timestamp": "2024-01-01T00:02:00Z"}
]
replay_validation: true
timeout: 45
tags: ["sourcing", "replay"]
- name: "message_ordering_test"
description: "Test message ordering guarantees"
test_type: "tool_call"
target: "message_order_validator"
parameters:
message_count: 1000
ordering_key: "user_id"
validation_type: "strict"
timeout: 90
tags: ["messaging", "ordering"]
- name: "External System Integration"
description: "Integration with external systems and third-party services"
enabled: true
tags: ["external", "third_party", "integration"]
parallel: true
timeout: 300
tests:
- name: "database_integration_test"
description: "Multi-database integration testing"
test_type: "tool_call"
target: "database_coordinator"
parameters:
databases: [
{"type": "postgresql", "name": "primary"},
{"type": "redis", "name": "cache"},
{"type": "mongodb", "name": "analytics"}
]
operations: ["read", "write", "transaction", "backup"]
timeout: 60
tags: ["database", "multi_db"]
- name: "payment_gateway_integration"
description: "Payment gateway integration testing"
test_type: "tool_call"
target: "payment_gateway"
parameters:
gateway: "stripe"
test_scenarios: [
{"type": "successful_payment", "amount": 100},
{"type": "declined_card", "amount": 200},
{"type": "expired_card", "amount": 150}
]
webhook_validation: true
timeout: 90
tags: ["payment", "gateway"]
- name: "email_service_integration"
description: "Email service provider integration"
test_type: "tool_call"
target: "email_service"
parameters:
provider: "sendgrid"
email_types: ["transactional", "marketing", "notification"]
template_validation: true
delivery_tracking: true
timeout: 45
tags: ["email", "service"]
- name: "monitoring_system_integration"
description: "Monitoring and observability system integration"
test_type: "tool_call"
target: "monitoring_integration"
parameters:
systems: ["prometheus", "grafana", "jaeger", "elasticsearch"]
metrics_validation: true
alerting_test: true
timeout: 60
tags: ["monitoring", "observability"]
- name: "Service Mesh and Discovery"
description: "Service mesh integration and service discovery testing"
enabled: true
tags: ["service_mesh", "discovery", "networking"]
parallel: true
timeout: 200
tests:
- name: "service_mesh_routing"
description: "Test service mesh routing and load balancing"
test_type: "tool_call"
target: "mesh_router"
parameters:
mesh_provider: "istio"
routing_rules: [
{"service": "user_service", "weight": 80, "version": "v1"},
{"service": "user_service", "weight": 20, "version": "v2"}
]
load_balancing: "round_robin"
timeout: 60
tags: ["mesh", "routing"]
- name: "circuit_breaker_integration"
description: "Test circuit breaker patterns in service mesh"
test_type: "tool_call"
target: "circuit_breaker"
parameters:
failure_threshold: 5
timeout: 30
half_open_requests: 3
target_service: "payment_service"
timeout: 90
tags: ["circuit_breaker", "resilience"]
- name: "service_discovery_failover"
description: "Test service discovery and failover scenarios"
test_type: "tool_call"
target: "discovery_failover"
parameters:
primary_instance: "user_service_1"
backup_instances: ["user_service_2", "user_service_3"]
failover_time: 10
timeout: 60
tags: ["discovery", "failover"]
- name: "Performance and Scalability Integration"
description: "Integration performance testing under realistic load"
enabled: true
tags: ["performance", "scalability", "load"]
parallel: true
timeout: 400
tests:
- name: "end_to_end_performance"
description: "End-to-end workflow performance testing"
test_type: "tool_call"
target: "e2e_performance"
parameters:
workflow: "complete_order_process"
concurrent_users: 100
test_duration: 300
sla_requirements: {
"max_response_time": 5000,
"min_throughput": 50,
"max_error_rate": 0.01
}
enable_progress: true
timeout: 360
tags: ["e2e", "performance"]
- name: "service_scaling_test"
description: "Test service auto-scaling behavior"
test_type: "tool_call"
target: "scaling_validator"
parameters:
scaling_policy: "cpu_based"
min_instances: 2
max_instances: 10
scale_up_threshold: 70
scale_down_threshold: 30
timeout: 240
tags: ["scaling", "auto_scaling"]
- name: "database_connection_pooling"
description: "Test database connection pooling under load"
test_type: "tool_call"
target: "connection_pool_test"
parameters:
pool_size: 20
concurrent_connections: 100
connection_lifecycle: "managed"
leak_detection: true
timeout: 120
tags: ["database", "pooling"]
# Integration testing variables
variables:
# Service URLs and commands
USER_SERVICE_CMD: "python -m user_service --port 8001 --env integration"
ORDER_SERVICE_CMD: "python -m order_service --port 8002 --env integration"
PAYMENT_SERVICE_CMD: "python -m payment_service --port 8003 --env integration"
INVENTORY_SERVICE_CMD: "python -m inventory_service --port 8004 --env integration"
NOTIFICATION_SERVICE_CMD: "python -m notification_service --port 8005 --env integration"
# Database connections
USER_DB_URL: "postgresql://test_user:password@localhost:5432/users_test"
ORDER_DB_URL: "postgresql://test_user:password@localhost:5432/orders_test"
INVENTORY_DB_URL: "postgresql://test_user:password@localhost:5432/inventory_test"
CACHE_URL: "redis://localhost:6379/0"
# Message queue and external services
MQ_URL: "amqp://guest:guest@localhost:5672/"
PAYMENT_GATEWAY: "https://api.sandbox.stripe.com"
EMAIL_PROVIDER: "sendgrid_test"
SMS_PROVIDER: "twilio_test"
# Authentication tokens
PAYMENT_SERVICE_TOKEN: "${PAYMENT_TOKEN}"
PAYMENT_ENCRYPTION_KEY: "${ENCRYPTION_KEY}"
# Test environment
ENVIRONMENT: "integration"
# Dynamic values from test execution
USER_ID_FROM_REGISTRATION: "dynamic"
ORDER_ID_FROM_PLACEMENT: "dynamic"
ORDER_TOTAL: "dynamic"
# Integration Testing Best Practices:
#
# 1. Service Dependency Management:
# - Use depends_on to ensure proper startup order
# - Validate service health before running tests
# - Implement proper cleanup between test runs
#
# 2. Test Data Management:
# - Use test-specific databases and clean state
# - Implement data factories for consistent test data
# - Clean up test data after each test run
#
# 3. External System Mocking:
# - Use test/sandbox environments for external services
# - Mock external dependencies when full integration isn't possible
# - Validate contract compliance with real services
#
# 4. Error Scenario Testing:
# - Test failure modes and recovery scenarios
# - Validate circuit breaker and timeout behaviors
# - Test partial failure scenarios
#
# 5. Performance Considerations:
# - Include realistic load in integration tests
# - Monitor resource usage across all services
# - Validate SLA requirements under integration load
#
# Execution Examples:
#
# Full integration suite:
# mcptesta yaml integration_config.yaml --parallel 8 --output ./integration_results
#
# Workflow-focused testing:
# mcptesta yaml integration_config.yaml --tag workflows --tag e2e
#
# Performance integration testing:
# mcptesta yaml integration_config.yaml --tag performance --enable-profiling
#
# External system integration only:
# mcptesta yaml integration_config.yaml --tag external --tag third_party
#
# Service mesh testing:
# mcptesta yaml integration_config.yaml --tag service_mesh --tag discovery

View File

@ -0,0 +1,275 @@
# MCPTesta Intermediate Configuration Template
#
# This template demonstrates intermediate features including:
# - Multiple test suites with dependencies
# - Basic MCP protocol features (notifications, progress)
# - Error handling and validation
# - HTML reporting and output management
# - Environment variable usage
# Global configuration
config:
parallel_workers: 4
output_directory: "./test_results"
output_format: "html" # Generate HTML reports
global_timeout: 180
max_concurrent_operations: 8
# Enable advanced features
features:
test_notifications: true
test_progress: true
test_cancellation: false # Enable when ready
test_sampling: false
# Retry policy for flaky tests
retry_policy:
max_retries: 2
backoff_factor: 1.5
retry_on_errors: ["ConnectionError", "TimeoutError"]
# Multiple server configurations
servers:
- name: "primary_server"
command: "${SERVER_COMMAND:python -m my_fastmcp_server}"
transport: "stdio"
timeout: 30
enabled: true
env_vars:
DEBUG: "${DEBUG_MODE:0}"
LOG_LEVEL: "${LOG_LEVEL:INFO}"
- name: "backup_server"
command: "${BACKUP_SERVER_COMMAND:python -m my_fastmcp_server --port 8081}"
transport: "stdio"
timeout: 30
enabled: false # Enable when needed
# Test suites with progressive complexity
test_suites:
- name: "Prerequisites"
description: "Essential setup and connectivity tests"
enabled: true
tags: ["setup", "prerequisite"]
parallel: false # Run sequentially for setup
timeout: 60
tests:
- name: "server_startup"
description: "Verify server starts and responds"
test_type: "ping"
target: ""
timeout: 10
tags: ["startup"]
- name: "capability_discovery"
description: "Discover all server capabilities"
test_type: "tool_call"
target: "list_tools"
timeout: 15
tags: ["discovery"]
depends_on: ["server_startup"]
- name: "Core Tool Testing"
description: "Comprehensive tool testing with validation"
enabled: true
tags: ["tools", "core"]
parallel: true
timeout: 120
setup:
validate_connection: true
discover_capabilities: true
tests:
- name: "echo_simple"
description: "Basic echo functionality"
test_type: "tool_call"
target: "echo"
parameters:
message: "${TEST_MESSAGE:Hello, World!}"
expected:
message: "${TEST_MESSAGE:Hello, World!}"
timeout: 10
tags: ["echo", "basic"]
depends_on: ["capability_discovery"]
- name: "echo_with_progress"
description: "Echo with progress monitoring"
test_type: "tool_call"
target: "echo"
parameters:
message: "Testing progress reporting"
simulate_work: true
enable_progress: true
timeout: 20
tags: ["echo", "progress"]
depends_on: ["echo_simple"]
- name: "parameterized_tool"
description: "Tool with complex parameters"
test_type: "tool_call"
target: "process_data" # Replace with actual tool
parameters:
data:
items: [1, 2, 3, 4, 5]
options:
format: "json"
validate: true
metadata:
source: "mcptesta"
timestamp: "2024-01-01T00:00:00Z"
expected:
success: true
processed_count: 5
timeout: 25
tags: ["complex", "data"]
retry_count: 1
- name: "Resource Management"
description: "Test resource reading and management"
enabled: true
tags: ["resources"]
parallel: true
timeout: 90
tests:
- name: "read_configuration"
description: "Read server configuration"
test_type: "resource_read"
target: "config://server.json"
timeout: 15
tags: ["config"]
expected:
content_type: "application/json"
- name: "read_file_resource"
description: "Read file system resource"
test_type: "resource_read"
target: "file://${CONFIG_FILE:./config.yml}"
timeout: 15
tags: ["filesystem"]
- name: "resource_with_parameters"
description: "Parameterized resource reading"
test_type: "resource_read"
target: "data://query"
parameters:
query: "SELECT * FROM items LIMIT 5"
format: "json"
timeout: 20
tags: ["database", "query"]
- name: "Prompt Testing"
description: "Test prompt generation and templating"
enabled: true
tags: ["prompts"]
parallel: true
timeout: 60
tests:
- name: "simple_prompt"
description: "Basic prompt generation"
test_type: "prompt_get"
target: "greeting"
parameters:
name: "${USER_NAME:MCPTesta User}"
context: "testing"
expected:
messages_count: ">0"
timeout: 15
tags: ["greeting"]
- name: "template_prompt"
description: "Complex template with variables"
test_type: "prompt_get"
target: "analysis"
parameters:
subject: "FastMCP server performance"
data_points: ["latency", "throughput", "error_rate"]
analysis_type: "comprehensive"
timeout: 20
tags: ["analysis", "template"]
- name: "Notification Testing"
description: "Test notification subscription and handling"
enabled: true
tags: ["notifications", "advanced"]
parallel: false # Sequential for proper notification testing
timeout: 90
tests:
- name: "subscribe_notifications"
description: "Subscribe to resource change notifications"
test_type: "notification"
target: "resources_list_changed"
timeout: 30
tags: ["subscription"]
- name: "trigger_notification"
description: "Trigger a notification event"
test_type: "tool_call"
target: "update_resource" # Tool that triggers notifications
parameters:
resource_id: "test_resource"
action: "update"
timeout: 15
tags: ["trigger"]
depends_on: ["subscribe_notifications"]
- name: "Error Handling"
description: "Test error conditions and edge cases"
enabled: true
tags: ["errors", "validation"]
parallel: true
timeout: 60
tests:
- name: "invalid_tool"
description: "Test non-existent tool error"
test_type: "tool_call"
target: "non_existent_tool"
expected_error: "Tool not found"
timeout: 10
tags: ["invalid"]
- name: "malformed_parameters"
description: "Test parameter validation"
test_type: "tool_call"
target: "echo"
parameters:
invalid_param: "should_fail"
expected_error: "Invalid parameters"
timeout: 10
tags: ["validation"]
- name: "timeout_handling"
description: "Test timeout behavior"
test_type: "tool_call"
target: "slow_operation"
parameters:
delay: 20
timeout: 5 # Will timeout
expected_error: "timeout"
tags: ["timeout"]
# Variables for customization and environment-specific values
variables:
SERVER_COMMAND: "python -m my_fastmcp_server"
BACKUP_SERVER_COMMAND: "python -m my_fastmcp_server --backup"
TEST_MESSAGE: "Intermediate testing with MCPTesta"
USER_NAME: "MCPTesta User"
CONFIG_FILE: "./server_config.yml"
DEBUG_MODE: "1"
LOG_LEVEL: "DEBUG"
# Configuration Tips:
# 1. Use ${VARIABLE:default_value} syntax for flexible configurations
# 2. Set enabled: false for tests you're not ready to run
# 3. Use depends_on to create test execution order
# 4. Tags help organize and filter tests
# 5. HTML reports provide better visualization: output_format: "html"
#
# Run specific test suites:
# mcptesta yaml config.yaml --tag core
# mcptesta yaml config.yaml --exclude-tag advanced

View File

@ -0,0 +1,549 @@
# MCPTesta Stress Testing Configuration Template
#
# Specialized template for comprehensive stress testing and performance validation.
# Designed to push FastMCP servers to their limits and identify bottlenecks.
#
# Stress Testing Categories:
# - Load testing with various patterns
# - Performance benchmarking
# - Resource exhaustion testing
# - Concurrency and parallelism limits
# - Memory and CPU pressure testing
# - Network stress and bandwidth testing
# Stress testing optimized configuration
config:
parallel_workers: 16 # High concurrency for stress testing
output_directory: "./stress_test_results"
output_format: "all"
global_timeout: 1800 # 30 minutes for long-running stress tests
max_concurrent_operations: 100
# Stress testing specific features
enable_stress_testing: true
enable_memory_profiling: true
enable_performance_profiling: true
enable_resource_monitoring: true
features:
test_notifications: true
test_cancellation: true
test_progress: true
test_sampling: true
# Aggressive retry policy for stress conditions
retry_policy:
max_retries: 1 # Minimal retries to avoid masking stress failures
backoff_factor: 1.0
retry_on_errors: ["ConnectionError"]
# Performance monitoring configuration
monitoring:
enable_real_time_metrics: true
metrics_collection_interval: 1 # Collect metrics every second
performance_thresholds:
max_latency_ms: 5000 # Allow higher latency under stress
max_memory_mb: 2048
max_cpu_percent: 95
resource_sampling_rate: 0.1 # Sample 10% of operations for detailed metrics
# Multiple server instances for distributed load testing
servers:
- name: "stress_target_1"
command: "${STRESS_SERVER_1_CMD:python -m my_fastmcp_server --performance-mode --instance 1}"
transport: "stdio"
timeout: 60
enabled: true
env_vars:
PERFORMANCE_MODE: "true"
MAX_CONNECTIONS: "1000"
BUFFER_SIZE: "65536"
GC_THRESHOLD: "high"
- name: "stress_target_2"
command: "${STRESS_SERVER_2_CMD:python -m my_fastmcp_server --performance-mode --instance 2}"
transport: "stdio"
timeout: 60
enabled: true
env_vars:
PERFORMANCE_MODE: "true"
INSTANCE_ID: "2"
- name: "stress_target_3"
command: "${STRESS_SERVER_3_CMD:python -m my_fastmcp_server --performance-mode --instance 3}"
transport: "stdio"
timeout: 60
enabled: false # Enable for multi-instance testing
# Comprehensive stress testing suites
test_suites:
- name: "Baseline Performance Measurement"
description: "Establish performance baseline before stress testing"
enabled: true
tags: ["baseline", "performance"]
parallel: false # Sequential for accurate baseline
timeout: 300
tests:
- name: "single_operation_latency"
description: "Measure single operation latency"
test_type: "tool_call"
target: "echo"
parameters:
message: "baseline_test"
retry_count: 1000 # Multiple samples for statistical significance
timeout: 120
tags: ["latency", "baseline"]
- name: "throughput_measurement"
description: "Measure maximum throughput"
test_type: "tool_call"
target: "echo"
parameters:
message: "throughput_test"
retry_count: 10000
enable_progress: true
timeout: 300
tags: ["throughput", "baseline"]
- name: "resource_usage_baseline"
description: "Measure baseline resource usage"
test_type: "tool_call"
target: "resource_monitor"
parameters:
duration: 60
metrics: ["cpu", "memory", "io", "network"]
timeout: 90
tags: ["resources", "baseline"]
- name: "Load Pattern Testing"
description: "Test various load patterns and traffic shapes"
enabled: true
tags: ["load", "patterns"]
parallel: true
timeout: 900
tests:
- name: "constant_load_test"
description: "Sustained constant load testing"
test_type: "tool_call"
target: "echo"
parameters:
message: "constant_load_${ITERATION}"
retry_count: 50000 # 50k operations
timeout: 600
tags: ["constant", "sustained"]
- name: "spike_load_test"
description: "Sudden traffic spike testing"
test_type: "tool_call"
target: "spike_handler"
parameters:
spike_factor: 10
spike_duration: 30
baseline_rps: 100
enable_progress: true
timeout: 120
tags: ["spike", "burst"]
- name: "ramp_up_test"
description: "Gradual load ramp-up testing"
test_type: "tool_call"
target: "ramp_processor"
parameters:
start_rps: 1
end_rps: 1000
ramp_duration: 300
hold_duration: 60
enable_progress: true
timeout: 480
tags: ["ramp", "gradual"]
- name: "oscillating_load_test"
description: "Oscillating load pattern testing"
test_type: "tool_call"
target: "oscillator"
parameters:
min_rps: 10
max_rps: 500
period_seconds: 60
cycles: 10
enable_progress: true
timeout: 720
tags: ["oscillating", "variable"]
- name: "Concurrency Stress Testing"
description: "High concurrency and parallelism stress testing"
enabled: true
tags: ["concurrency", "parallel"]
parallel: true
timeout: 600
tests:
- name: "maximum_concurrent_connections"
description: "Test maximum concurrent connection limits"
test_type: "tool_call"
target: "connection_holder"
parameters:
hold_duration: 120
connection_type: "persistent"
retry_count: 1000 # Attempt 1000 concurrent connections
timeout: 180
tags: ["connections", "limits"]
- name: "thread_pool_exhaustion"
description: "Test thread pool exhaustion and recovery"
test_type: "tool_call"
target: "thread_consumer"
parameters:
threads_to_consume: 500
hold_duration: 60
timeout: 120
tags: ["threads", "exhaustion"]
- name: "async_operation_flood"
description: "Flood server with async operations"
test_type: "tool_call"
target: "async_processor"
parameters:
async_operations: 10000
operation_type: "concurrent"
enable_progress: true
timeout: 300
tags: ["async", "flood"]
- name: "request_queue_overflow"
description: "Test request queue overflow handling"
test_type: "tool_call"
target: "queue_filler"
parameters:
queue_size_target: 100000
overflow_strategy: "backpressure"
timeout: 180
tags: ["queue", "overflow"]
- name: "Memory Stress Testing"
description: "Memory-intensive operations and pressure testing"
enabled: true
tags: ["memory", "stress"]
parallel: true
timeout: 800
tests:
- name: "large_payload_processing"
description: "Process increasingly large payloads"
test_type: "tool_call"
target: "payload_processor"
parameters:
payload_sizes: ["1MB", "10MB", "100MB", "500MB"]
processing_type: "memory_intensive"
enable_progress: true
timeout: 600
tags: ["payload", "large"]
- name: "memory_leak_detection"
description: "Long-running test to detect memory leaks"
test_type: "tool_call"
target: "memory_allocator"
parameters:
allocation_pattern: "incremental"
test_duration: 1800 # 30 minutes
leak_detection: true
enable_progress: true
timeout: 2000
tags: ["leaks", "long_running"]
- name: "garbage_collection_pressure"
description: "Create GC pressure and measure impact"
test_type: "tool_call"
target: "gc_stress_tester"
parameters:
allocation_rate: "high"
object_lifetime: "mixed"
gc_frequency_target: 100
timeout: 300
tags: ["gc", "pressure"]
- name: "out_of_memory_recovery"
description: "Test OOM recovery mechanisms"
test_type: "tool_call"
target: "oom_simulator"
parameters:
memory_limit: "512MB"
allocation_strategy: "aggressive"
recovery_validation: true
expected_error: "out of memory"
timeout: 120
tags: ["oom", "recovery"]
- name: "CPU Intensive Stress Testing"
description: "CPU-bound operations and computational stress"
enabled: true
tags: ["cpu", "computational"]
parallel: true
timeout: 600
tests:
- name: "cpu_bound_operations"
description: "CPU-intensive computational tasks"
test_type: "tool_call"
target: "cpu_intensive_task"
parameters:
operation_type: "prime_calculation"
complexity: "high"
iterations: 1000000
retry_count: 10 # Multiple CPU-bound tasks
timeout: 300
tags: ["cpu_bound", "computation"]
- name: "algorithm_complexity_test"
description: "Test algorithmic complexity under load"
test_type: "tool_call"
target: "algorithm_tester"
parameters:
algorithms: ["sorting", "searching", "graph_traversal"]
input_sizes: [1000, 10000, 100000]
complexity_analysis: true
enable_progress: true
timeout: 400
tags: ["algorithms", "complexity"]
- name: "multi_core_utilization"
description: "Test multi-core CPU utilization"
test_type: "tool_call"
target: "parallel_processor"
parameters:
cores_to_utilize: "all"
workload_distribution: "balanced"
cpu_affinity: "round_robin"
timeout: 240
tags: ["multicore", "utilization"]
- name: "I/O Stress Testing"
description: "Intensive I/O operations and bandwidth testing"
enabled: true
tags: ["io", "bandwidth"]
parallel: true
timeout: 700
tests:
- name: "disk_io_stress"
description: "Intensive disk I/O operations"
test_type: "tool_call"
target: "disk_io_tester"
parameters:
io_pattern: "random_write"
file_size: "1GB"
block_size: "4KB"
concurrent_operations: 100
enable_progress: true
timeout: 600
tags: ["disk", "io"]
- name: "network_bandwidth_test"
description: "Network bandwidth saturation testing"
test_type: "tool_call"
target: "bandwidth_tester"
parameters:
data_volume: "10GB"
connection_count: 50
transfer_pattern: "bulk"
enable_progress: true
timeout: 400
tags: ["network", "bandwidth"]
- name: "file_descriptor_exhaustion"
description: "Test file descriptor limit handling"
test_type: "tool_call"
target: "fd_consumer"
parameters:
target_fd_count: 10000
fd_type: "mixed"
cleanup_strategy: "gradual"
timeout: 180
tags: ["file_descriptors", "limits"]
- name: "Error Handling Under Stress"
description: "Error handling and recovery under stress conditions"
enabled: true
tags: ["errors", "recovery", "stress"]
parallel: true
timeout: 400
tests:
- name: "error_flood_test"
description: "Flood server with error-inducing requests"
test_type: "tool_call"
target: "error_generator"
parameters:
error_types: ["invalid_params", "timeout", "resource_unavailable"]
error_rate: 0.5 # 50% error rate
total_operations: 10000
timeout: 300
tags: ["errors", "flood"]
- name: "cascading_failure_stress"
description: "Test cascading failure handling under stress"
test_type: "tool_call"
target: "cascade_simulator"
parameters:
initial_failure_rate: 0.1
cascade_probability: 0.3
recovery_time: 30
timeout: 240
tags: ["cascading", "failures"]
- name: "timeout_storm_test"
description: "Multiple simultaneous timeout scenarios"
test_type: "tool_call"
target: "timeout_generator"
parameters:
timeout_patterns: ["random", "burst", "gradual"]
concurrent_timeouts: 100
timeout: 180
tags: ["timeouts", "storm"]
- name: "Resource Exhaustion Testing"
description: "Systematic resource exhaustion and recovery testing"
enabled: true
tags: ["resources", "exhaustion"]
parallel: true
timeout: 900
tests:
- name: "connection_pool_exhaustion"
description: "Exhaust connection pool resources"
test_type: "tool_call"
target: "connection_exhaustor"
parameters:
pool_size: 100
hold_duration: 300
exhaustion_strategy: "gradual"
timeout: 400
tags: ["connections", "pool"]
- name: "buffer_overflow_test"
description: "Test buffer overflow handling"
test_type: "tool_call"
target: "buffer_tester"
parameters:
buffer_sizes: ["64KB", "1MB", "10MB"]
overflow_data: "random"
safety_mechanisms: true
timeout: 180
tags: ["buffers", "overflow"]
- name: "cache_thrashing_test"
description: "Induce cache thrashing and measure impact"
test_type: "tool_call"
target: "cache_thrasher"
parameters:
cache_size: "100MB"
working_set: "1GB"
access_pattern: "random"
timeout: 300
tags: ["cache", "thrashing"]
- name: "Long Duration Stability Testing"
description: "Extended duration stability and endurance testing"
enabled: true
tags: ["stability", "endurance", "soak"]
parallel: false # Sequential for stability testing
timeout: 7200 # 2 hours
tests:
- name: "soak_test_24h"
description: "24-hour soak test simulation"
test_type: "tool_call"
target: "soak_tester"
parameters:
duration: 3600 # 1 hour for demo (would be 86400 for full 24h)
operations_per_minute: 60
stability_monitoring: true
enable_progress: true
timeout: 3900
tags: ["soak", "24h", "stability"]
- name: "resource_leak_detection"
description: "Long-running resource leak detection"
test_type: "tool_call"
target: "leak_detector"
parameters:
monitoring_duration: 1800 # 30 minutes
leak_types: ["memory", "connections", "file_handles"]
detection_threshold: 0.05 # 5% growth threshold
enable_progress: true
timeout: 2000
tags: ["leaks", "monitoring"]
# Stress testing specific variables
variables:
# Server configurations optimized for stress testing
STRESS_SERVER_1_CMD: "python -m my_fastmcp_server --performance-mode --max-connections 1000 --instance 1"
STRESS_SERVER_2_CMD: "python -m my_fastmcp_server --performance-mode --max-connections 1000 --instance 2"
STRESS_SERVER_3_CMD: "python -m my_fastmcp_server --performance-mode --max-connections 1000 --instance 3"
# Load testing parameters
MAX_RPS: "10000"
STRESS_DURATION: "1800" # 30 minutes
RAMP_DURATION: "300" # 5 minutes
# Resource limits for testing
MAX_MEMORY_MB: "2048"
MAX_CPU_PERCENT: "95"
MAX_CONNECTIONS: "1000"
MAX_FILE_DESCRIPTORS: "10000"
# Payload sizes for testing
SMALL_PAYLOAD: "1KB"
MEDIUM_PAYLOAD: "1MB"
LARGE_PAYLOAD: "100MB"
XLARGE_PAYLOAD: "500MB"
# Test iteration counters
ITERATION: "0"
BATCH_ID: "stress_batch_1"
# Stress Testing Execution Guide:
#
# 1. Baseline Establishment:
# - Always run baseline tests first
# - Document performance metrics before stress testing
# - Establish SLA thresholds
#
# 2. Progressive Load Testing:
# - Start with lower loads and increase gradually
# - Monitor resource utilization continuously
# - Identify breaking points and bottlenecks
#
# 3. Resource Monitoring:
# - Enable all profiling and monitoring features
# - Watch for memory leaks, CPU spikes, I/O bottlenecks
# - Monitor system metrics beyond application metrics
#
# 4. Failure Analysis:
# - Document failure modes and recovery patterns
# - Test error handling under stress conditions
# - Validate graceful degradation mechanisms
#
# 5. Long Duration Testing:
# - Run soak tests to detect stability issues
# - Monitor for gradual resource leaks
# - Validate system behavior over extended periods
#
# Execution Examples:
#
# Full stress test suite:
# mcptesta yaml stress_config.yaml --parallel 16 --timeout 7200
#
# Memory-focused stress testing:
# mcptesta yaml stress_config.yaml --tag memory --enable-memory-profiling
#
# Load pattern testing only:
# mcptesta yaml stress_config.yaml --tag load --tag patterns
#
# Long duration stability testing:
# mcptesta yaml stress_config.yaml --tag stability --tag endurance
#
# CPU stress testing:
# mcptesta yaml stress_config.yaml --tag cpu --tag computational --parallel 8

192
pyproject.toml Normal file
View File

@ -0,0 +1,192 @@
[project]
name = "mcptesta"
version = "0.1.0"
description = "Comprehensive FastMCP Test Client for testing FastMCP servers and MCP protocol features"
authors = [
{name = "Developer", email = "dev@example.com"}
]
readme = "README.md"
license = {text = "MIT"}
requires-python = ">=3.11"
keywords = ["mcp", "testing", "fastmcp", "protocol", "automation", "client"]
classifiers = [
"Development Status :: 4 - Beta",
"Intended Audience :: Developers",
"License :: OSI Approved :: MIT License",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Topic :: Software Development :: Testing",
"Topic :: Communications",
]
dependencies = [
"fastmcp>=0.9.0",
"pydantic>=2.0.0",
"click>=8.0.0",
"rich>=13.0.0",
"asyncio-throttle>=1.0.0",
"aiofiles>=23.0.0",
"pyyaml>=6.0.0",
"psutil>=5.9.0",
"pytest>=7.0.0",
"pytest-asyncio>=0.21.0",
"jsonschema>=4.0.0",
"tabulate>=0.9.0",
"websockets>=12.0",
"httpx>=0.25.0",
]
[project.optional-dependencies]
dev = [
"pytest>=7.0.0",
"pytest-asyncio>=0.21.0",
"pytest-cov>=4.0.0",
"black>=23.0.0",
"ruff>=0.1.0",
"mypy>=1.0.0",
"pre-commit>=3.0.0",
]
performance = [
"memory-profiler>=0.61.0",
"line-profiler>=4.0.0",
"py-spy>=0.3.14",
]
visualization = [
"matplotlib>=3.5.0",
"seaborn>=0.12.0",
"plotly>=5.15.0",
]
[project.scripts]
mcptesta = "mcptesta.cli:main"
mcptesta-server = "mcptesta.server:main"
[project.urls]
Homepage = "https://github.com/example/mcptesta"
Documentation = "https://mcptesta.readthedocs.io"
Repository = "https://github.com/example/mcptesta"
Issues = "https://github.com/example/mcptesta/issues"
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[tool.hatch.build.targets.wheel]
packages = ["src/mcptesta"]
[tool.hatch.build.targets.sdist]
include = [
"/src",
"/tests",
"/examples",
"/docs",
]
# Testing configuration
[tool.pytest.ini_options]
minversion = "7.0"
addopts = "-ra -q --strict-markers --strict-config"
testpaths = ["tests"]
python_files = ["test_*.py", "*_test.py"]
python_classes = ["Test*"]
python_functions = ["test_*"]
asyncio_mode = "auto"
markers = [
"unit: Unit tests",
"integration: Integration tests",
"performance: Performance tests",
"slow: Slow tests",
"network: Tests requiring network access",
"mcp: MCP protocol tests",
"parallel: Tests that can run in parallel",
"yaml: YAML configuration tests",
"cli: CLI interface tests",
"notification: Notification system tests",
"cancellation: Cancellation feature tests",
"sampling: Sampling feature tests",
"auth: Authentication tests",
"stress: Stress testing",
]
# Code formatting
[tool.black]
line-length = 88
target-version = ['py311']
include = '\.pyi?$'
extend-exclude = '''
/(
# directories
\.eggs
| \.git
| \.hg
| \.mypy_cache
| \.tox
| \.venv
| build
| dist
)/
'''
# Linting
[tool.ruff]
target-version = "py311"
line-length = 88
select = [
"E", # pycodestyle errors
"W", # pycodestyle warnings
"F", # pyflakes
"I", # isort
"B", # flake8-bugbear
"C4", # flake8-comprehensions
"UP", # pyupgrade
"ARG", # flake8-unused-arguments
"SIM", # flake8-simplify
"TCH", # flake8-type-checking
"N", # pep8-naming
]
ignore = [
"E501", # line too long, handled by black
"B008", # do not perform function calls in argument defaults
]
[tool.ruff.per-file-ignores]
"tests/**/*" = ["ARG", "S101"]
# Type checking
[tool.mypy]
python_version = "3.11"
check_untyped_defs = true
disallow_any_generics = true
disallow_incomplete_defs = true
disallow_untyped_defs = true
no_implicit_optional = true
show_error_codes = true
warn_redundant_casts = true
warn_unused_ignores = true
warn_return_any = true
[[tool.mypy.overrides]]
module = "tests.*"
disallow_untyped_defs = false
# Coverage
[tool.coverage.run]
source = ["src"]
omit = [
"*/tests/*",
"*/test_*",
"*/__pycache__/*",
]
[tool.coverage.report]
exclude_lines = [
"pragma: no cover",
"def __repr__",
"raise AssertionError",
"raise NotImplementedError",
"if __name__ == .__main__.:",
]

210
scripts/generate-logo-exports.sh Executable file
View File

@ -0,0 +1,210 @@
#!/bin/bash
# MCPTesta Logo Export Generation Script
# Generates comprehensive logo asset collection from master SVG files
set -e # Exit on any error
echo "🧪 MCPTesta Logo Export Generation"
echo "=================================="
# Color definitions for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
PURPLE='\033[0;35m'
NC='\033[0m' # No Color
# Check dependencies
command -v magick >/dev/null 2>&1 || {
echo -e "${RED}Error: ImageMagick is required but not installed.${NC}" >&2
echo "Install with: brew install imagemagick (macOS) or apt-get install imagemagick (Ubuntu)"
exit 1
}
command -v xmllint >/dev/null 2>&1 || {
echo -e "${YELLOW}Warning: xmllint not found. SVG validation will be skipped.${NC}" >&2
}
# Project directories
PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
LOGO_DIR="$PROJECT_ROOT/assets/logo"
SOURCE_DIR="$LOGO_DIR/source"
cd "$PROJECT_ROOT"
# Verify master SVG exists
MASTER_SVG="$SOURCE_DIR/mcptesta-logo-master.svg"
if [ ! -f "$MASTER_SVG" ]; then
echo -e "${RED}Error: Master SVG not found at $MASTER_SVG${NC}"
echo "Please create the master SVG file first using the specifications in:"
echo " - logo-design-specs.md"
echo " - logo-export-specifications.md"
exit 1
fi
echo -e "${GREEN}✓ Master SVG found${NC}"
# Validate SVG if xmllint is available
if command -v xmllint >/dev/null 2>&1; then
if xmllint --noout "$MASTER_SVG" 2>/dev/null; then
echo -e "${GREEN}✓ Master SVG is valid${NC}"
else
echo -e "${RED}Error: Master SVG is not valid XML${NC}"
exit 1
fi
fi
echo ""
echo -e "${BLUE}📱 Generating Favicon Package...${NC}"
# Generate favicon sizes
cd "$LOGO_DIR/favicons"
magick "$MASTER_SVG" -resize 16x16 -strip favicon-16x16.png
magick "$MASTER_SVG" -resize 32x32 -strip favicon-32x32.png
magick "$MASTER_SVG" -resize 48x48 -strip favicon-48x48.png
# Create multi-resolution ICO
magick favicon-16x16.png favicon-32x32.png favicon-48x48.png favicon.ico
# Copy SVG favicon for modern browsers
cp "$MASTER_SVG" favicon.svg
# Apple touch icon
magick "$MASTER_SVG" -resize 180x180 -strip apple-touch-icon.png
# Android chrome icon
magick "$MASTER_SVG" -resize 192x192 -strip android-chrome-192x192.png
echo -e "${GREEN}✓ Favicon package complete${NC}"
echo ""
echo -e "${BLUE}📱 Generating iOS App Icons...${NC}"
# iOS App Store sizes
cd "$LOGO_DIR/app-icons/ios"
declare -a ios_sizes=("57" "114" "120" "180" "1024")
for size in "${ios_sizes[@]}"; do
magick "$MASTER_SVG" -resize ${size}x${size} -strip icon-${size}x${size}.png
echo " Generated iOS ${size}x${size}"
done
echo -e "${GREEN}✓ iOS icons complete${NC}"
echo ""
echo -e "${BLUE}🤖 Generating Android App Icons...${NC}"
# Android Play Store sizes
cd "$LOGO_DIR/app-icons/android"
declare -a android_sizes=("72" "96" "144" "192" "512")
for size in "${android_sizes[@]}"; do
magick "$MASTER_SVG" -resize ${size}x${size} -strip icon-${size}x${size}.png
echo " Generated Android ${size}x${size}"
done
echo -e "${GREEN}✓ Android icons complete${NC}"
echo ""
echo -e "${BLUE}🌐 Generating Web Assets...${NC}"
# Web-optimized sizes
cd "$LOGO_DIR/web"
declare -a web_sizes=("64" "128" "256" "512")
for size in "${web_sizes[@]}"; do
magick "$MASTER_SVG" -resize ${size}x${size} -strip mcptesta-logo-${size}px.png
echo " Generated web ${size}px"
done
# Copy optimized SVG for web
cp "$MASTER_SVG" mcptesta-logo.svg
echo -e "${GREEN}✓ Web assets complete${NC}"
echo ""
echo -e "${BLUE}📱 Generating Social Media Assets...${NC}"
cd "$LOGO_DIR/social"
# Profile picture (square)
magick "$MASTER_SVG" -resize 400x400 -strip profile-400x400.png
# Social media card (with background and text)
magick -size 1200x630 xc:"#6B46C1" \
\( "$MASTER_SVG" -resize 300x300 \) \
-gravity west -geometry +150+0 -composite \
-font Arial-Bold -pointsize 64 -fill white \
-gravity center -annotate +200+0 "MCPTesta" \
-font Arial -pointsize 32 -fill "#E2E8F0" \
-gravity center -annotate +200+80 "Community-driven testing excellence" \
card-1200x630.png
# GitHub social preview
magick -size 1280x640 xc:"#0D1117" \
\( "$MASTER_SVG" -resize 240x240 \) \
-gravity west -geometry +120+0 -composite \
-font Arial-Bold -pointsize 54 -fill white \
-gravity center -annotate +250+0 "MCPTesta" \
-font Arial -pointsize 28 -fill "#8B949E" \
-gravity center -annotate +250+60 "FastMCP Testing Framework" \
github-social-1280x640.png
# Twitter header
magick -size 1500x500 gradient:"#6B46C1-#8B5CF6" \
\( "$MASTER_SVG" -resize 200x200 \) \
-gravity west -geometry +100+0 -composite \
-font Arial-Bold -pointsize 48 -fill white \
-gravity center -annotate +200+0 "MCPTesta" \
-font Arial -pointsize 24 -fill "#E2E8F0" \
-gravity center -annotate +200+50 "Community-driven testing excellence for MCP" \
header-1500x500.png
echo -e "${GREEN}✓ Social media assets complete${NC}"
echo ""
echo -e "${BLUE}🎨 Generating Theme Variants...${NC}"
# Note: Theme variants would require separate SVG files with different colors
# This is a placeholder for manual theme variant creation
cd "$LOGO_DIR/theme-variants"
echo " Dark theme variants: Pending manual creation"
echo " Light theme variants: Pending manual creation"
echo " High contrast variants: Pending manual creation"
echo -e "${YELLOW}⚠ Theme variants require manual SVG creation with adjusted colors${NC}"
echo ""
echo -e "${BLUE}📊 Generating Asset Summary...${NC}"
# Count generated files
total_files=0
for dir in favicons app-icons/ios app-icons/android web social; do
count=$(find "$LOGO_DIR/$dir" -name "*.png" -o -name "*.ico" -o -name "*.svg" | wc -l)
total_files=$((total_files + count))
echo " $dir: $count files"
done
echo ""
echo -e "${GREEN}🎉 Logo Export Generation Complete!${NC}"
echo -e "${PURPLE}Generated $total_files asset files${NC}"
echo ""
echo -e "${BLUE}📋 Next Steps:${NC}"
echo "1. Review generated assets in assets/logo/"
echo "2. Create theme variant SVG files manually"
echo "3. Run quality assurance: ./scripts/qa-logo-check.sh"
echo "4. Integrate into documentation and project files"
echo ""
echo -e "${BLUE}🔧 Manual Tasks Remaining:${NC}"
echo "• Create horizontal layout SVG (logo + text)"
echo "• Design dark theme color variants"
echo "• Design light theme color variants"
echo "• Create high-contrast accessibility versions"
echo "• Generate print-ready CMYK files"
echo ""
echo -e "${GREEN}Asset generation script completed successfully!${NC} 🧪"

161
scripts/health-check.sh Executable file
View File

@ -0,0 +1,161 @@
#!/bin/sh
# MCPTesta Documentation Health Check Script
# Comprehensive health validation for container monitoring
set -e
# Configuration
HOST=${HOST:-localhost}
PORT=${PORT:-4321}
TIMEOUT=${HEALTH_TIMEOUT:-10}
MAX_RESPONSE_TIME=5000 # milliseconds
# Colors for output (simplified for sh compatibility)
RED='\033[0;31m'
GREEN='\033[0;32m'
NC='\033[0m'
# Logging functions
log() {
echo "[$(date +'%Y-%m-%d %H:%M:%S')] $1"
}
success() {
echo "${GREEN}[HEALTHY]${NC} $1"
}
error() {
echo "${RED}[UNHEALTHY]${NC} $1" >&2
exit 1
}
# Health check functions
check_port() {
if ! nc -z "$HOST" "$PORT" 2>/dev/null; then
error "Port $PORT is not accessible on $HOST"
fi
log "Port $PORT is accessible"
}
check_http_response() {
local response_code
local response_time
# Check HTTP response with timeout
if ! response_code=$(wget --spider --server-response --timeout="$TIMEOUT" --tries=1 \
"http://$HOST:$PORT/" 2>&1 | grep "HTTP/" | tail -1 | awk '{print $2}'); then
error "HTTP request failed or timed out"
fi
# Validate response code
if [ "$response_code" != "200" ]; then
error "HTTP response code: $response_code (expected: 200)"
fi
log "HTTP response: $response_code OK"
}
check_response_time() {
local start_time
local end_time
local response_time
start_time=$(date +%s%3N)
if ! wget --spider --quiet --timeout="$TIMEOUT" --tries=1 "http://$HOST:$PORT/" 2>/dev/null; then
error "Response time check failed"
fi
end_time=$(date +%s%3N)
response_time=$((end_time - start_time))
if [ "$response_time" -gt "$MAX_RESPONSE_TIME" ]; then
error "Response time too slow: ${response_time}ms (max: ${MAX_RESPONSE_TIME}ms)"
fi
log "Response time: ${response_time}ms"
}
check_content() {
local content
# Check if the page contains expected content
if ! content=$(wget --quiet --timeout="$TIMEOUT" --tries=1 -O- "http://$HOST:$PORT/" 2>/dev/null); then
error "Failed to retrieve page content"
fi
# Basic content validation
if ! echo "$content" | grep -q "MCPTesta"; then
error "Page content validation failed - 'MCPTesta' not found"
fi
if ! echo "$content" | grep -q "<html"; then
error "Page content validation failed - HTML structure not found"
fi
log "Content validation passed"
}
check_dependencies() {
# Check if required commands are available
command -v wget >/dev/null 2>&1 || error "wget command not found"
command -v nc >/dev/null 2>&1 || error "nc (netcat) command not found"
log "Required dependencies available"
}
check_memory_usage() {
local memory_usage
local memory_limit_mb=512 # Default limit
# Get memory usage in MB (simplified check)
if [ -f /proc/meminfo ]; then
memory_usage=$(awk '/MemAvailable/ {printf "%.0f", $2/1024}' /proc/meminfo)
if [ "$memory_usage" -lt 50 ]; then
error "Low available memory: ${memory_usage}MB"
fi
log "Available memory: ${memory_usage}MB"
else
log "Memory check skipped (no /proc/meminfo)"
fi
}
check_disk_space() {
local disk_usage
local disk_limit=90 # 90% threshold
# Check disk usage of /app
if disk_usage=$(df /app 2>/dev/null | tail -1 | awk '{print $5}' | sed 's/%//'); then
if [ "$disk_usage" -gt "$disk_limit" ]; then
error "High disk usage: ${disk_usage}% (limit: ${disk_limit}%)"
fi
log "Disk usage: ${disk_usage}%"
else
log "Disk check skipped"
fi
}
# Main health check routine
main() {
log "Starting comprehensive health check..."
log "Target: http://$HOST:$PORT/"
log "Timeout: ${TIMEOUT}s"
# Run all health checks
check_dependencies
check_memory_usage
check_disk_space
check_port
check_http_response
check_response_time
check_content
success "All health checks passed"
log "Container is healthy and ready to serve requests"
}
# Run health check
main

127
scripts/start-docs.sh Executable file
View File

@ -0,0 +1,127 @@
#!/bin/bash
# MCPTesta Documentation Startup Script
# Handles initialization and environment setup
set -euo pipefail
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Logging function
log() {
echo -e "${BLUE}[$(date +'%Y-%m-%d %H:%M:%S')]${NC} $1"
}
error() {
echo -e "${RED}[ERROR]${NC} $1" >&2
}
success() {
echo -e "${GREEN}[SUCCESS]${NC} $1"
}
warn() {
echo -e "${YELLOW}[WARN]${NC} $1"
}
# Environment setup
NODE_ENV=${NODE_ENV:-development}
HOST=${HOST:-0.0.0.0}
PORT=${PORT:-4321}
log "Starting MCPTesta Documentation Server"
log "Environment: $NODE_ENV"
log "Host: $HOST"
log "Port: $PORT"
# Ensure we're in the correct directory
cd /app
# Check if node_modules exists
if [ ! -d "node_modules" ]; then
warn "node_modules not found, installing dependencies..."
npm ci
success "Dependencies installed"
fi
# Check if package.json exists
if [ ! -f "package.json" ]; then
error "package.json not found in /app"
exit 1
fi
# Validate Astro configuration
if [ ! -f "astro.config.mjs" ]; then
error "astro.config.mjs not found"
exit 1
fi
# Health check function
health_check() {
local max_attempts=30
local attempt=1
log "Waiting for server to be ready..."
while [ $attempt -le $max_attempts ]; do
if curl -f -s "http://localhost:$PORT/" > /dev/null 2>&1; then
success "Server is ready!"
return 0
fi
log "Attempt $attempt/$max_attempts - Server not ready yet..."
sleep 2
((attempt++))
done
error "Server failed to start within expected time"
return 1
}
# Start server based on environment
if [ "$NODE_ENV" = "development" ]; then
log "Starting development server with hot reloading..."
# Start server in background for health check
npm run dev:verbose -- --host "$HOST" --port "$PORT" &
SERVER_PID=$!
# Wait for server to be ready
if health_check; then
success "Development server started successfully"
# Bring server to foreground
wait $SERVER_PID
else
error "Failed to start development server"
kill $SERVER_PID 2>/dev/null || true
exit 1
fi
elif [ "$NODE_ENV" = "production" ]; then
log "Building production assets..."
# Clean previous builds
npm run clean
# Build for production
npm run build:prod
if [ ! -d "dist" ]; then
error "Production build failed - dist directory not found"
exit 1
fi
success "Production build completed"
log "Starting production server..."
# Start preview server
npm run preview -- --host "$HOST" --port "$PORT"
else
error "Unknown NODE_ENV: $NODE_ENV (expected: development or production)"
exit 1
fi

236
scripts/validate-setup.sh Executable file
View File

@ -0,0 +1,236 @@
#!/bin/bash
# MCPTesta Docker Setup Validation Script
# Validates the complete Docker environment setup
set -euo pipefail
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
# Logging functions
log() {
echo -e "${BLUE}[INFO]${NC} $1"
}
success() {
echo -e "${GREEN}[✓]${NC} $1"
}
error() {
echo -e "${RED}[✗]${NC} $1" >&2
}
warn() {
echo -e "${YELLOW}[!]${NC} $1"
}
# Validation functions
check_dependencies() {
log "Checking dependencies..."
if ! command -v docker >/dev/null 2>&1; then
error "Docker is not installed or not in PATH"
return 1
fi
success "Docker is available"
if ! command -v docker >/dev/null 2>&1 || ! docker compose version >/dev/null 2>&1; then
error "Docker Compose is not available"
return 1
fi
success "Docker Compose is available"
if ! command -v make >/dev/null 2>&1; then
error "Make is not installed"
return 1
fi
success "Make is available"
}
check_files() {
log "Checking required files..."
local required_files=(
".env"
"docker-compose.yml"
"docker-compose.dev.yml"
"docker-compose.prod.yml"
"Makefile"
"docs/Dockerfile"
"docs/package.json"
"docs/astro.config.mjs"
"scripts/health-check.sh"
"scripts/start-docs.sh"
)
for file in "${required_files[@]}"; do
if [ ! -f "$file" ]; then
error "Required file missing: $file"
return 1
fi
done
success "All required files present"
}
check_permissions() {
log "Checking file permissions..."
local executable_files=(
"scripts/health-check.sh"
"scripts/start-docs.sh"
"scripts/validate-setup.sh"
)
for file in "${executable_files[@]}"; do
if [ ! -x "$file" ]; then
error "File not executable: $file"
return 1
fi
done
success "All executable files have correct permissions"
}
check_docker_daemon() {
log "Checking Docker daemon..."
if ! docker info >/dev/null 2>&1; then
error "Docker daemon is not running or not accessible"
return 1
fi
success "Docker daemon is running"
}
check_networks() {
log "Checking Docker networks..."
if ! docker network ls | grep -q "caddy"; then
warn "Caddy network not found - will be created"
if ! docker network create caddy >/dev/null 2>&1; then
error "Failed to create caddy network"
return 1
fi
success "Caddy network created"
else
success "Caddy network exists"
fi
}
check_compose_config() {
log "Validating Docker Compose configuration..."
if ! docker compose config >/dev/null 2>&1; then
error "Docker Compose configuration is invalid"
return 1
fi
success "Docker Compose configuration is valid"
}
check_env_file() {
log "Checking environment configuration..."
if [ ! -f ".env" ]; then
error ".env file not found"
return 1
fi
# Check required environment variables
local required_vars=(
"COMPOSE_PROJECT"
"NODE_ENV"
"DOCS_DOMAIN"
"DOCS_PORT"
"DOCS_HOST"
)
for var in "${required_vars[@]}"; do
if ! grep -q "^$var=" .env; then
error "Required environment variable missing: $var"
return 1
fi
done
success "Environment configuration is valid"
}
check_docs_structure() {
log "Checking documentation structure..."
local required_docs_files=(
"docs/src"
"docs/astro.config.mjs"
"docs/package.json"
)
for item in "${required_docs_files[@]}"; do
if [ ! -e "docs/$item" ] && [ ! -e "$item" ]; then
error "Documentation structure incomplete: $item"
return 1
fi
done
success "Documentation structure is complete"
}
show_next_steps() {
echo ""
log "Setup validation completed successfully!"
echo ""
echo -e "${GREEN}Next steps:${NC}"
echo "1. Start development environment: ${BLUE}make dev${NC}"
echo "2. View logs: ${BLUE}make logs-live${NC}"
echo "3. Access documentation: ${BLUE}http://localhost:4321${NC}"
echo "4. Check container status: ${BLUE}make status${NC}"
echo ""
echo -e "${GREEN}Additional commands:${NC}"
echo "• Switch to production: ${BLUE}make env-prod && make prod${NC}"
echo "• View all commands: ${BLUE}make help${NC}"
echo "• Debug setup: ${BLUE}make debug${NC}"
echo ""
}
# Main validation routine
main() {
echo -e "${BLUE}MCPTesta Docker Setup Validation${NC}"
echo "=================================="
echo ""
# Change to project directory
cd "$(dirname "$0")/.."
# Run all validation checks
local checks=(
"check_dependencies"
"check_docker_daemon"
"check_files"
"check_permissions"
"check_env_file"
"check_docs_structure"
"check_networks"
"check_compose_config"
)
local failed=0
for check in "${checks[@]}"; do
if ! $check; then
((failed++))
fi
done
echo ""
if [ $failed -eq 0 ]; then
success "All validation checks passed!"
show_next_steps
else
error "$failed validation check(s) failed"
echo ""
echo -e "${YELLOW}Please fix the issues above and run the validation again.${NC}"
exit 1
fi
}
# Run validation
main "$@"

46
src/mcptesta/__init__.py Normal file
View File

@ -0,0 +1,46 @@
"""
MCPTesta - Comprehensive FastMCP Test Client
A powerful testing framework for FastMCP servers and MCP protocol features.
Supports CLI parameters, YAML test configurations, parallel execution,
and comprehensive reporting.
"""
__version__ = "0.1.0"
__author__ = "MCPTesta Team"
__email__ = "dev@example.com"
from .core.client import MCPTestClient
from .core.config import TestConfig, ServerConfig, GlobalConfig, TestFeatures, OutputConfig, ExecutionConfig
from .core.session import TestSession, SessionMetrics, SessionState
from .protocol.features import ProtocolFeatures
from .yaml_parser.parser import YAMLTestParser
from .runners.parallel import ParallelTestRunner
from .reporters.console import ConsoleReporter
from .reporters.html import HTMLReporter
from .utils.logging import setup_logging, get_logger
from .utils.metrics import MetricsCollector
__all__ = [
"__version__",
"__author__",
"__email__",
"MCPTestClient",
"TestConfig",
"ServerConfig",
"GlobalConfig",
"TestFeatures",
"OutputConfig",
"ExecutionConfig",
"TestSession",
"SessionMetrics",
"SessionState",
"ProtocolFeatures",
"YAMLTestParser",
"ParallelTestRunner",
"ConsoleReporter",
"HTMLReporter",
"setup_logging",
"get_logger",
"MetricsCollector",
]

432
src/mcptesta/cli.py Normal file
View File

@ -0,0 +1,432 @@
"""
MCPTesta CLI Interface
Command-line interface for testing FastMCP servers with comprehensive options
for configuration, parallel execution, and advanced MCP protocol features.
"""
import asyncio
import sys
from pathlib import Path
from typing import List, Optional, Dict, Any
import click
from rich.console import Console
from rich.panel import Panel
from rich.text import Text
from .core.config import TestConfig, ServerConfig
from .core.session import TestSession
from .yaml_parser.parser import YAMLTestParser
from .runners.parallel import ParallelTestRunner
from .reporters.console import ConsoleReporter
from .reporters.html import HTMLReporter
from .utils.logging import setup_logging, LoggingConfig
from .utils.validation import validate_server_connection
console = Console()
def show_banner():
"""Display MCPTesta banner with version info"""
banner_text = Text()
banner_text.append("🧪 MCPTesta ", style="bold cyan")
banner_text.append("v0.1.0", style="bold white")
banner_text.append(" - FastMCP Test Client", style="cyan")
description = Text()
description.append("Comprehensive testing framework for FastMCP servers\n", style="dim")
description.append("• CLI parameters & YAML configurations\n", style="dim")
description.append("• Parallel execution & advanced reporting\n", style="dim")
description.append("• Full MCP protocol feature support", style="dim")
panel = Panel(
Text.assemble(banner_text, "\n\n", description),
border_style="cyan",
padding=(1, 2)
)
console.print(panel)
@click.group(invoke_without_command=True)
@click.option("--version", is_flag=True, help="Show version information")
@click.option("--verbose", "-v", count=True, help="Increase verbosity level")
@click.pass_context
def main(ctx: click.Context, version: bool, verbose: int):
"""
MCPTesta - Comprehensive FastMCP Test Client
Test FastMCP servers with CLI parameters or YAML configurations.
Supports parallel execution, advanced reporting, and full MCP protocol features.
"""
# Setup logging based on verbosity
import logging
log_level = logging.WARNING
if verbose >= 1:
log_level = logging.INFO
if verbose >= 2:
log_level = logging.DEBUG
log_config = LoggingConfig(
level=log_level,
console_output=True,
use_rich_console=True,
rich_tracebacks=True
)
setup_logging(log_config)
if version:
console.print(f"MCPTesta version 0.1.0")
return
if ctx.invoked_subcommand is None:
show_banner()
console.print("\nUse --help to see available commands", style="dim")
@main.command()
@click.option("--server", "-s", required=True, help="Server command or connection string")
@click.option("--transport", "-t", type=click.Choice(["stdio", "sse", "ws"]), default="stdio", help="Transport protocol")
@click.option("--timeout", default=30, help="Connection timeout in seconds")
@click.option("--parallel", "-p", default=1, help="Number of parallel test workers")
@click.option("--output", "-o", type=click.Path(), help="Output directory for reports")
@click.option("--format", "output_format", type=click.Choice(["console", "html", "json", "junit"]), default="console", help="Output format")
@click.option("--include-tools", help="Comma-separated list of tools to test")
@click.option("--exclude-tools", help="Comma-separated list of tools to exclude")
@click.option("--test-notifications", is_flag=True, help="Test notification features")
@click.option("--test-cancellation", is_flag=True, help="Test cancellation features")
@click.option("--test-progress", is_flag=True, help="Test progress reporting")
@click.option("--test-sampling", is_flag=True, help="Test sampling features")
@click.option("--test-auth", is_flag=True, help="Test authentication")
@click.option("--auth-token", help="Authentication token for testing")
@click.option("--max-concurrent", default=10, help="Maximum concurrent operations")
@click.option("--stress-test", is_flag=True, help="Enable stress testing mode")
@click.option("--memory-profile", is_flag=True, help="Enable memory profiling")
@click.option("--performance-profile", is_flag=True, help="Enable performance profiling")
def test(
server: str,
transport: str,
timeout: int,
parallel: int,
output: Optional[str],
output_format: str,
include_tools: Optional[str],
exclude_tools: Optional[str],
test_notifications: bool,
test_cancellation: bool,
test_progress: bool,
test_sampling: bool,
test_auth: bool,
auth_token: Optional[str],
max_concurrent: int,
stress_test: bool,
memory_profile: bool,
performance_profile: bool,
):
"""Test a FastMCP server with CLI parameters"""
console.print(f"🚀 Testing FastMCP server: {server}", style="bold green")
# Build configuration using the new structured approach
test_config = TestConfig.from_cli_args(
server=server,
transport=transport,
timeout=timeout,
auth_token=auth_token,
parallel=parallel,
output=output,
output_format=output_format,
include_tools=include_tools.split(",") if include_tools else None,
exclude_tools=exclude_tools.split(",") if exclude_tools else None,
test_notifications=test_notifications,
test_cancellation=test_cancellation,
test_progress=test_progress,
test_sampling=test_sampling,
test_auth=test_auth,
max_concurrent=max_concurrent,
stress_test=stress_test,
memory_profile=memory_profile,
performance_profile=performance_profile,
)
# Run tests
asyncio.run(_run_tests(test_config))
@main.command()
@click.argument("config_path", type=click.Path(exists=True, path_type=Path))
@click.option("--parallel", "-p", help="Override parallel workers from config")
@click.option("--output", "-o", type=click.Path(), help="Override output directory")
@click.option("--format", "output_format", type=click.Choice(["console", "html", "json", "junit"]), help="Override output format")
@click.option("--dry-run", is_flag=True, help="Validate configuration without running tests")
@click.option("--list-tests", is_flag=True, help="List all tests that would be run")
@click.option("--filter", help="Filter tests by name pattern")
@click.option("--tag", multiple=True, help="Run only tests with specified tags")
@click.option("--exclude-tag", multiple=True, help="Exclude tests with specified tags")
def yaml(
config_path: Path,
parallel: Optional[int],
output: Optional[str],
output_format: Optional[str],
dry_run: bool,
list_tests: bool,
filter: Optional[str],
tag: List[str],
exclude_tag: List[str],
):
"""Test using YAML configuration file"""
console.print(f"📄 Loading YAML configuration: {config_path}", style="bold green")
try:
# Parse YAML configuration
parser = YAMLTestParser()
test_config = parser.parse_file(config_path)
# Apply CLI overrides
if parallel:
test_config.parallel_workers = parallel
if output:
test_config.output_directory = output
if output_format:
test_config.output_format = output_format
# Apply filtering
if filter or tag or exclude_tag:
test_config.apply_filters(
name_pattern=filter,
include_tags=list(tag),
exclude_tags=list(exclude_tag)
)
if dry_run:
console.print("✅ Configuration validated successfully", style="green")
console.print(f"Found {len(test_config.test_suites)} test suites", style="dim")
return
if list_tests:
_list_tests(test_config)
return
# Run tests
asyncio.run(_run_tests(test_config))
except Exception as e:
console.print(f"❌ Configuration error: {e}", style="red")
sys.exit(1)
@main.command()
@click.option("--server", "-s", required=True, help="Server command or connection string")
@click.option("--transport", "-t", type=click.Choice(["stdio", "sse", "ws"]), default="stdio", help="Transport protocol")
@click.option("--timeout", default=10, help="Connection timeout in seconds")
def validate(server: str, transport: str, timeout: int):
"""Validate server connection and list capabilities"""
console.print(f"🔍 Validating server connection: {server}", style="bold blue")
server_config = ServerConfig(
command=server,
transport=transport,
timeout=timeout,
)
asyncio.run(_validate_server(server_config))
@main.command()
@click.option("--count", default=100, help="Number of ping requests")
@click.option("--interval", default=1.0, help="Interval between pings (seconds)")
@click.option("--server", "-s", required=True, help="Server command or connection string")
@click.option("--transport", "-t", type=click.Choice(["stdio", "sse", "ws"]), default="stdio", help="Transport protocol")
def ping(count: int, interval: float, server: str, transport: str):
"""Ping server to test connectivity and latency"""
console.print(f"🏓 Pinging server: {server}", style="bold yellow")
server_config = ServerConfig(
command=server,
transport=transport,
)
asyncio.run(_ping_server(server_config, count, interval))
@main.command()
@click.argument("template", type=click.Choice(["basic", "intermediate", "advanced", "expert", "stress", "integration"]))
@click.argument("output_path", type=click.Path(path_type=Path))
@click.option("--server-command", help="Custom server command for template")
@click.option("--test-types", help="Comma-separated list of test types (tool_call,resource_read,prompt_get)")
@click.option("--parallel-workers", type=int, help="Number of parallel workers")
@click.option("--enable-features", help="Comma-separated list of features (notifications,progress,cancellation,sampling)")
def generate_config(
template: str,
output_path: Path,
server_command: Optional[str],
test_types: Optional[str],
parallel_workers: Optional[int],
enable_features: Optional[str]
):
"""Generate YAML configuration template
Available template types:
\b
- basic: Simple template for beginners
- intermediate: Mid-level template with dependencies
- advanced: Full-featured template with all capabilities
- expert: Maximum complexity for expert users
- stress: Specialized performance and stress testing
- integration: Multi-service integration testing
"""
console.print(f"📝 Generating {template} configuration template", style="bold cyan")
from .yaml_parser.templates import generate_template
try:
# Build custom parameters
custom_kwargs = {}
if server_command:
custom_kwargs["server_command"] = server_command
if test_types:
custom_kwargs["test_types"] = [t.strip() for t in test_types.split(",")]
if parallel_workers:
custom_kwargs["parallel_workers"] = parallel_workers
if enable_features:
custom_kwargs["enable_features"] = [f.strip() for f in enable_features.split(",")]
# Generate template
config_content = generate_template(template, **custom_kwargs)
output_path.write_text(config_content)
console.print(f"✅ Configuration saved to: {output_path}", style="green")
console.print(f"📋 Template type: {template}", style="dim")
# Show template information
from .yaml_parser.templates import get_template_info
info = get_template_info(template)
if info:
console.print(f"📝 Description: {info.get('description', 'N/A')}", style="dim")
console.print(f"🔧 Features: {', '.join(info.get('features', []))}", style="dim")
console.print(f"🎯 Use case: {info.get('use_case', 'N/A')}", style="dim")
except Exception as e:
console.print(f"❌ Generation error: {e}", style="red")
sys.exit(1)
async def _run_tests(config: TestConfig):
"""Run tests with given configuration"""
try:
# Initialize test session
session = TestSession(config)
# Initialize reporters
reporters = []
if config.output_format in ["console", "all"]:
reporters.append(ConsoleReporter())
if config.output_format in ["html", "all"]:
reporters.append(HTMLReporter(config.output_directory))
# Run tests
if config.parallel_workers > 1:
runner = ParallelTestRunner(config, reporters)
else:
from .runners.sequential import SequentialTestRunner
runner = SequentialTestRunner(config, reporters)
results = await runner.run(session)
# Display summary
_display_summary(results)
# Exit with appropriate code
if results.has_failures():
sys.exit(1)
except KeyboardInterrupt:
console.print("\n⚠️ Tests interrupted by user", style="yellow")
sys.exit(130)
except Exception as e:
console.print(f"❌ Test execution error: {e}", style="red")
sys.exit(1)
async def _validate_server(config: ServerConfig):
"""Validate server connection"""
try:
capabilities = await validate_server_connection(config)
console.print("✅ Server connection successful", style="green")
console.print("\n📋 Server Capabilities:")
if capabilities.get("tools"):
console.print(f" 🔧 Tools: {len(capabilities['tools'])} available")
for tool in capabilities["tools"][:5]: # Show first 5
console.print(f"{tool.get('name', 'Unknown')}", style="dim")
if len(capabilities["tools"]) > 5:
console.print(f" ... and {len(capabilities['tools']) - 5} more", style="dim")
if capabilities.get("resources"):
console.print(f" 📚 Resources: {len(capabilities['resources'])} available")
if capabilities.get("prompts"):
console.print(f" 💬 Prompts: {len(capabilities['prompts'])} available")
if capabilities.get("server_info"):
info = capabilities["server_info"]
console.print(f" Server: {info.get('name', 'Unknown')} v{info.get('version', 'Unknown')}")
except Exception as e:
console.print(f"❌ Validation failed: {e}", style="red")
sys.exit(1)
async def _ping_server(config: ServerConfig, count: int, interval: float):
"""Ping server for connectivity testing"""
from .protocol.ping import PingTester
try:
tester = PingTester(config)
results = await tester.ping_multiple(count, interval)
# Display results
console.print(f"\n📊 Ping Statistics:")
console.print(f" Sent: {results['sent']}")
console.print(f" Received: {results['received']}")
console.print(f" Lost: {results['lost']} ({results['loss_percent']:.1f}%)")
if results['latencies']:
console.print(f" Min: {min(results['latencies']):.2f}ms")
console.print(f" Max: {max(results['latencies']):.2f}ms")
console.print(f" Avg: {sum(results['latencies'])/len(results['latencies']):.2f}ms")
except Exception as e:
console.print(f"❌ Ping failed: {e}", style="red")
sys.exit(1)
def _list_tests(config: TestConfig):
"""List all tests that would be run"""
console.print("📋 Tests to be executed:")
total_tests = 0
for suite in config.test_suites:
console.print(f"\n🔧 {suite.name}:", style="bold")
for test in suite.tests:
console.print(f"{test.name}", style="dim")
total_tests += 1
console.print(f"\nTotal: {total_tests} tests", style="bold green")
def _display_summary(results: Any):
"""Display test execution summary"""
console.print("\n" + "="*60)
console.print("📊 Test Execution Summary", style="bold cyan")
console.print("="*60)
# Add summary display logic here
console.print(f"Tests run: {results.total_tests}")
console.print(f"Passed: {results.passed}", style="green")
console.print(f"Failed: {results.failed}", style="red" if results.failed > 0 else "green")
console.print(f"Skipped: {results.skipped}", style="yellow" if results.skipped > 0 else "dim")
if results.execution_time:
console.print(f"Execution time: {results.execution_time:.2f}s")
if __name__ == "__main__":
main()

View File

@ -0,0 +1,18 @@
"""
MCPTesta Core Components
Core functionality for MCPTesta including configuration management,
client connections, and session handling.
"""
from .config import TestConfig, ServerConfig, GlobalConfig
from .client import MCPTestClient
from .session import TestSession
__all__ = [
"TestConfig",
"ServerConfig",
"GlobalConfig",
"MCPTestClient",
"TestSession",
]

491
src/mcptesta/core/client.py Normal file
View File

@ -0,0 +1,491 @@
"""
MCPTesta Test Client
Advanced test client for FastMCP servers with comprehensive protocol support,
parallel execution, and advanced features like cancellation, progress, sampling.
"""
import asyncio
import time
import uuid
from datetime import datetime
from typing import Dict, Any, List, Optional, Union, AsyncIterator
from dataclasses import dataclass, field
from contextlib import asynccontextmanager
from fastmcp import FastMCP
from fastmcp.client import Client
from pydantic import BaseModel
from .config import ServerConfig
from ..protocol.features import ProtocolFeatures
from ..utils.logging import get_logger
from ..utils.metrics import MetricsCollector
@dataclass
class TestResult:
"""Result of a single test execution"""
test_name: str
success: bool
execution_time: float
error_message: Optional[str] = None
response_data: Optional[Any] = None
metadata: Dict[str, Any] = field(default_factory=dict)
timestamp: datetime = field(default_factory=datetime.now)
@dataclass
class ServerCapabilities:
"""Server capabilities discovered during connection"""
tools: List[Dict[str, Any]] = field(default_factory=list)
resources: List[Dict[str, Any]] = field(default_factory=list)
prompts: List[Dict[str, Any]] = field(default_factory=list)
server_info: Dict[str, Any] = field(default_factory=dict)
supports_notifications: bool = False
supports_cancellation: bool = False
supports_progress: bool = False
supports_sampling: bool = False
class MCPTestClient:
"""
Advanced test client for FastMCP servers.
Features:
- Protocol feature detection and testing
- Parallel operation execution
- Advanced MCP features (cancellation, progress, sampling)
- Comprehensive metrics and logging
- Connection lifecycle management
- Error handling and retry logic
"""
def __init__(self,
server_config: ServerConfig,
enable_metrics: bool = True,
enable_logging: bool = True,
metrics_collector: Optional[MetricsCollector] = None):
self.server_config = server_config
self.logger = get_logger(__name__) if enable_logging else None
# Use provided metrics collector or create new one
if metrics_collector:
self.metrics = metrics_collector
elif enable_metrics:
self.metrics = MetricsCollector()
else:
self.metrics = None
self._client: Optional[Client] = None
self._capabilities: Optional[ServerCapabilities] = None
self._connection_start: Optional[float] = None
self._active_operations: Dict[str, Any] = {}
self._notification_handlers: Dict[str, callable] = {}
# Protocol feature support
self.protocol_features = ProtocolFeatures()
@asynccontextmanager
async def connect(self):
"""Async context manager for server connection"""
try:
await self._establish_connection()
yield self
finally:
await self._close_connection()
async def _establish_connection(self):
"""Establish connection to FastMCP server"""
if self.logger:
self.logger.info(f"Connecting to server: {self.server_config.command}")
start_time = time.time()
try:
# Create FastMCP client based on transport type
if self.server_config.transport == "stdio":
self._client = Client(self.server_config.command)
elif self.server_config.transport == "sse":
self._client = Client(f"sse://{self.server_config.command}")
elif self.server_config.transport == "ws":
self._client = Client(f"ws://{self.server_config.command}")
else:
raise ValueError(f"Unsupported transport: {self.server_config.transport}")
# Apply authentication if configured
if self.server_config.auth_token:
await self._configure_authentication()
# Establish connection
await self._client.connect()
connection_time = time.time() - start_time
self._connection_start = start_time
if self.metrics:
self.metrics.record_connection_time(connection_time)
if self.logger:
self.logger.info(f"Connected successfully in {connection_time:.3f}s")
# Discover server capabilities
await self._discover_capabilities()
except Exception as e:
if self.logger:
self.logger.error(f"Connection failed: {e}")
raise
async def _close_connection(self):
"""Close connection to server"""
if self._client:
try:
await self._client.close()
if self.logger:
self.logger.info("Connection closed")
except Exception as e:
if self.logger:
self.logger.warning(f"Error during connection close: {e}")
finally:
self._client = None
self._capabilities = None
self._connection_start = None
async def _configure_authentication(self):
"""Configure client authentication"""
if self.server_config.auth_type == "bearer":
# Set authorization header
if not hasattr(self._client, 'headers'):
self._client.headers = {}
self._client.headers["Authorization"] = f"Bearer {self.server_config.auth_token}"
elif self.server_config.auth_type == "oauth":
# OAuth flow (implementation depends on FastMCP OAuth support)
pass
async def _discover_capabilities(self):
"""Discover server capabilities and protocol features"""
capabilities = ServerCapabilities()
try:
# List tools
tools_response = await self._client.list_tools()
capabilities.tools = tools_response.get("tools", [])
# List resources
try:
resources_response = await self._client.list_resources()
capabilities.resources = resources_response.get("resources", [])
except Exception:
pass # Resources not supported
# List prompts
try:
prompts_response = await self._client.list_prompts()
capabilities.prompts = prompts_response.get("prompts", [])
except Exception:
pass # Prompts not supported
# Get server info
try:
server_info = await self._client.get_server_info()
capabilities.server_info = server_info
except Exception:
pass # Server info not available
# Test protocol feature support
capabilities.supports_notifications = await self.protocol_features.test_notifications(self._client)
capabilities.supports_cancellation = await self.protocol_features.test_cancellation(self._client)
capabilities.supports_progress = await self.protocol_features.test_progress(self._client)
capabilities.supports_sampling = await self.protocol_features.test_sampling(self._client)
self._capabilities = capabilities
if self.logger:
self.logger.info(f"Discovered {len(capabilities.tools)} tools, "
f"{len(capabilities.resources)} resources, "
f"{len(capabilities.prompts)} prompts")
except Exception as e:
if self.logger:
self.logger.warning(f"Capability discovery failed: {e}")
self._capabilities = ServerCapabilities() # Empty capabilities
async def call_tool(self,
tool_name: str,
parameters: Dict[str, Any] = None,
timeout: Optional[float] = None,
enable_cancellation: bool = False,
enable_progress: bool = False,
enable_sampling: bool = False,
sampling_rate: float = 1.0) -> TestResult:
"""Call a tool and return test result"""
start_time = time.time()
operation_id = str(uuid.uuid4())
test_name = f"tool_call_{tool_name}"
if parameters is None:
parameters = {}
try:
if self.logger:
self.logger.debug(f"Calling tool '{tool_name}' with parameters: {parameters}")
# Store active operation for potential cancellation
if enable_cancellation:
self._active_operations[operation_id] = {
"type": "tool_call",
"tool_name": tool_name,
"start_time": start_time
}
# Apply sampling if enabled
if enable_sampling and sampling_rate < 1.0:
import random
if random.random() > sampling_rate:
return TestResult(
test_name=test_name,
success=True,
execution_time=0.0,
metadata={"skipped_by_sampling": True}
)
# Call tool with progress monitoring if enabled
if enable_progress:
response = await self._call_tool_with_progress(tool_name, parameters, timeout)
else:
response = await asyncio.wait_for(
self._client.call_tool(tool_name, parameters),
timeout=timeout
)
execution_time = time.time() - start_time
if self.metrics:
self.metrics.record_tool_call(tool_name, execution_time, True)
return TestResult(
test_name=test_name,
success=True,
execution_time=execution_time,
response_data=response,
metadata={
"tool_name": tool_name,
"parameters": parameters,
"operation_id": operation_id
}
)
except asyncio.TimeoutError:
execution_time = time.time() - start_time
error_msg = f"Tool call timed out after {timeout}s"
if self.metrics:
self.metrics.record_tool_call(tool_name, execution_time, False)
return TestResult(
test_name=test_name,
success=False,
execution_time=execution_time,
error_message=error_msg,
metadata={"tool_name": tool_name, "timeout": True}
)
except Exception as e:
execution_time = time.time() - start_time
if self.metrics:
self.metrics.record_tool_call(tool_name, execution_time, False)
return TestResult(
test_name=test_name,
success=False,
execution_time=execution_time,
error_message=str(e),
metadata={"tool_name": tool_name, "exception_type": type(e).__name__}
)
finally:
# Clean up active operation
if operation_id in self._active_operations:
del self._active_operations[operation_id]
async def _call_tool_with_progress(self, tool_name: str, parameters: Dict[str, Any], timeout: Optional[float]) -> Any:
"""Call tool with progress monitoring"""
# Implementation depends on FastMCP progress support
# For now, fall back to regular call
return await self._client.call_tool(tool_name, parameters)
async def read_resource(self,
resource_uri: str,
timeout: Optional[float] = None) -> TestResult:
"""Read a resource and return test result"""
start_time = time.time()
test_name = f"resource_read_{resource_uri}"
try:
if self.logger:
self.logger.debug(f"Reading resource: {resource_uri}")
response = await asyncio.wait_for(
self._client.read_resource(resource_uri),
timeout=timeout
)
execution_time = time.time() - start_time
if self.metrics:
self.metrics.record_resource_read(resource_uri, execution_time, True)
return TestResult(
test_name=test_name,
success=True,
execution_time=execution_time,
response_data=response,
metadata={"resource_uri": resource_uri}
)
except Exception as e:
execution_time = time.time() - start_time
if self.metrics:
self.metrics.record_resource_read(resource_uri, execution_time, False)
return TestResult(
test_name=test_name,
success=False,
execution_time=execution_time,
error_message=str(e),
metadata={"resource_uri": resource_uri}
)
async def get_prompt(self,
prompt_name: str,
arguments: Dict[str, Any] = None,
timeout: Optional[float] = None) -> TestResult:
"""Get a prompt and return test result"""
start_time = time.time()
test_name = f"prompt_get_{prompt_name}"
if arguments is None:
arguments = {}
try:
if self.logger:
self.logger.debug(f"Getting prompt '{prompt_name}' with arguments: {arguments}")
response = await asyncio.wait_for(
self._client.get_prompt(prompt_name, arguments),
timeout=timeout
)
execution_time = time.time() - start_time
if self.metrics:
self.metrics.record_prompt_get(prompt_name, execution_time, True)
return TestResult(
test_name=test_name,
success=True,
execution_time=execution_time,
response_data=response,
metadata={"prompt_name": prompt_name, "arguments": arguments}
)
except Exception as e:
execution_time = time.time() - start_time
if self.metrics:
self.metrics.record_prompt_get(prompt_name, execution_time, False)
return TestResult(
test_name=test_name,
success=False,
execution_time=execution_time,
error_message=str(e),
metadata={"prompt_name": prompt_name}
)
async def ping(self, timeout: Optional[float] = None) -> TestResult:
"""Ping server for connectivity testing"""
start_time = time.time()
test_name = "ping"
try:
# Use list_tools as a lightweight ping operation
await asyncio.wait_for(
self._client.list_tools(),
timeout=timeout or 5.0
)
execution_time = time.time() - start_time
return TestResult(
test_name=test_name,
success=True,
execution_time=execution_time,
metadata={"latency_ms": execution_time * 1000}
)
except Exception as e:
execution_time = time.time() - start_time
return TestResult(
test_name=test_name,
success=False,
execution_time=execution_time,
error_message=str(e)
)
async def cancel_operation(self, operation_id: str) -> bool:
"""Cancel an active operation"""
if operation_id not in self._active_operations:
return False
try:
# Implementation depends on FastMCP cancellation support
# For now, just remove from active operations
del self._active_operations[operation_id]
if self.logger:
self.logger.info(f"Operation {operation_id} cancelled")
return True
except Exception as e:
if self.logger:
self.logger.error(f"Failed to cancel operation {operation_id}: {e}")
return False
def register_notification_handler(self, notification_type: str, handler: callable):
"""Register handler for notifications"""
self._notification_handlers[notification_type] = handler
@property
def capabilities(self) -> Optional[ServerCapabilities]:
"""Get discovered server capabilities"""
return self._capabilities
@property
def is_connected(self) -> bool:
"""Check if client is connected"""
return self._client is not None
@property
def connection_duration(self) -> Optional[float]:
"""Get connection duration in seconds"""
if self._connection_start:
return time.time() - self._connection_start
return None

600
src/mcptesta/core/config.py Normal file
View File

@ -0,0 +1,600 @@
"""
Configuration Models for MCPTesta
Comprehensive configuration system using Pydantic for type-safe configuration
management across CLI, YAML, and programmatic interfaces.
"""
import os
import re
from enum import Enum
from pathlib import Path
from typing import Dict, Any, List, Optional, Union, Set
from pydantic import BaseModel, Field, field_validator, model_validator
from pydantic.types import PositiveInt, NonNegativeInt, PositiveFloat
class TransportType(str, Enum):
"""Supported transport protocols for MCP communication"""
STDIO = "stdio"
SSE = "sse"
WS = "ws"
WEBSOCKET = "websocket" # Alias for ws
class OutputFormat(str, Enum):
"""Supported output formats for test results"""
CONSOLE = "console"
HTML = "html"
JSON = "json"
JUNIT = "junit"
ALL = "all"
class AuthType(str, Enum):
"""Supported authentication types"""
NONE = "none"
BEARER = "bearer"
BASIC = "basic"
OAUTH = "oauth"
CUSTOM = "custom"
class TestType(str, Enum):
"""Supported test types"""
PING = "ping"
TOOL_CALL = "tool_call"
RESOURCE_READ = "resource_read"
PROMPT_GET = "prompt_get"
NOTIFICATION = "notification"
CAPABILITY = "capability"
CUSTOM = "custom"
class LogLevel(str, Enum):
"""Logging levels"""
DEBUG = "debug"
INFO = "info"
WARNING = "warning"
ERROR = "error"
CRITICAL = "critical"
class AuthConfig(BaseModel):
"""Authentication configuration"""
auth_type: AuthType = AuthType.NONE
token: Optional[str] = None
username: Optional[str] = None
password: Optional[str] = None
headers: Dict[str, str] = Field(default_factory=dict)
oauth_config: Dict[str, Any] = Field(default_factory=dict)
@field_validator('token')
@classmethod
def validate_bearer_token(cls, v, info):
"""Validate bearer token format when auth_type is bearer"""
if info.data.get('auth_type') == AuthType.BEARER and v:
if not isinstance(v, str) or len(v.strip()) == 0:
raise ValueError("Bearer token must be a non-empty string")
return v
@field_validator('oauth_config')
@classmethod
def validate_oauth_config(cls, v, info):
"""Validate OAuth configuration"""
if info.data.get('auth_type') == AuthType.OAUTH:
required_fields = ['client_id', 'auth_url']
for field in required_fields:
if field not in v:
raise ValueError(f"OAuth configuration missing required field: {field}")
return v
model_config = {"use_enum_values": True}
class ServerConfig(BaseModel):
"""Configuration for a FastMCP server connection"""
name: str = Field(default="default", description="Server identifier")
command: str = Field(..., description="Command to start the server or connection string")
transport: TransportType = Field(default=TransportType.STDIO, description="Transport protocol")
timeout: PositiveInt = Field(default=30, description="Connection timeout in seconds")
# Environment and execution
env_vars: Dict[str, str] = Field(default_factory=dict, description="Environment variables")
working_directory: Optional[str] = Field(None, description="Working directory for server process")
# Authentication
auth: AuthConfig = Field(default_factory=AuthConfig, description="Authentication configuration")
auth_token: Optional[str] = Field(None, description="Legacy auth token field")
auth_type: Optional[AuthType] = Field(None, description="Legacy auth type field")
# Headers and metadata
headers: Dict[str, str] = Field(default_factory=dict, description="HTTP headers for transport")
metadata: Dict[str, Any] = Field(default_factory=dict, description="Additional metadata")
# Server options
enabled: bool = Field(default=True, description="Whether this server is enabled")
weight: PositiveFloat = Field(default=1.0, description="Load balancing weight")
max_connections: PositiveInt = Field(default=10, description="Maximum concurrent connections")
@field_validator('command')
@classmethod
def validate_command(cls, v):
"""Validate server command"""
if not v or not v.strip():
raise ValueError("Server command cannot be empty")
return v.strip()
@field_validator('working_directory')
@classmethod
def validate_working_directory(cls, v):
"""Validate working directory exists"""
if v and not Path(v).exists():
raise ValueError(f"Working directory does not exist: {v}")
return v
@field_validator('transport')
@classmethod
def normalize_transport(cls, v):
"""Normalize transport values"""
if v == TransportType.WEBSOCKET:
return TransportType.WS
return v
@model_validator(mode='after')
def handle_legacy_auth(self):
"""Handle legacy auth fields and migrate to new auth config"""
if self.auth_token or self.auth_type:
if self.auth_token:
self.auth.token = self.auth_token
if self.auth_type:
self.auth.auth_type = self.auth_type
# Clear legacy fields
self.auth_token = None
self.auth_type = None
return self
def get_connection_string(self) -> str:
"""Get formatted connection string for display"""
if self.transport == TransportType.STDIO:
return f"stdio://{self.command}"
elif self.transport in [TransportType.SSE, TransportType.WS]:
return f"{self.transport.value}://{self.command}"
return self.command
def get_env_with_defaults(self) -> Dict[str, str]:
"""Get environment variables merged with system environment"""
env = os.environ.copy()
env.update(self.env_vars)
return env
model_config = {"use_enum_values": True}
class TestFeatures(BaseModel):
"""Configuration for advanced MCP protocol features"""
test_notifications: bool = Field(default=False, description="Test notification features")
test_cancellation: bool = Field(default=False, description="Test cancellation features")
test_progress: bool = Field(default=False, description="Test progress reporting")
test_sampling: bool = Field(default=False, description="Test sampling features")
test_authentication: bool = Field(default=False, description="Test authentication")
test_capabilities: bool = Field(default=True, description="Test capability discovery")
# Advanced features
enable_stress_testing: bool = Field(default=False, description="Enable stress testing mode")
enable_performance_profiling: bool = Field(default=False, description="Enable performance profiling")
enable_memory_profiling: bool = Field(default=False, description="Enable memory profiling")
class RetryPolicy(BaseModel):
"""Retry policy configuration"""
max_retries: NonNegativeInt = Field(default=3, description="Maximum number of retries")
initial_delay: PositiveFloat = Field(default=1.0, description="Initial delay between retries (seconds)")
backoff_multiplier: PositiveFloat = Field(default=2.0, description="Exponential backoff multiplier")
max_delay: PositiveFloat = Field(default=60.0, description="Maximum delay between retries")
retry_on_timeout: bool = Field(default=True, description="Retry on timeout errors")
retry_on_connection_error: bool = Field(default=True, description="Retry on connection errors")
class OutputConfig(BaseModel):
"""Output configuration"""
format: OutputFormat = Field(default=OutputFormat.CONSOLE, description="Output format")
directory: Optional[str] = Field(None, description="Output directory for reports")
filename_pattern: str = Field(default="mcptesta_results_{timestamp}", description="Filename pattern")
include_timestamps: bool = Field(default=True, description="Include timestamps in output")
include_metadata: bool = Field(default=True, description="Include metadata in reports")
# Console-specific options
use_colors: bool = Field(default=True, description="Use colors in console output")
show_progress: bool = Field(default=True, description="Show progress indicators")
verbosity_level: LogLevel = Field(default=LogLevel.INFO, description="Console verbosity level")
# File output options
compress_output: bool = Field(default=False, description="Compress output files")
max_file_size: Optional[PositiveInt] = Field(None, description="Maximum file size in MB")
@field_validator('directory')
@classmethod
def validate_output_directory(cls, v):
"""Validate and create output directory"""
if v:
path = Path(v)
try:
path.mkdir(parents=True, exist_ok=True)
except Exception as e:
raise ValueError(f"Cannot create output directory {v}: {e}")
return v
def get_output_path(self, test_name: str = "default") -> Optional[Path]:
"""Get full output path for a test result"""
if not self.directory:
return None
import datetime
timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
filename = self.filename_pattern.format(
timestamp=timestamp,
test_name=test_name,
format=self.format if isinstance(self.format, str) else self.format.value
)
if self.format == OutputFormat.HTML:
filename += ".html"
elif self.format == OutputFormat.JSON:
filename += ".json"
elif self.format == OutputFormat.JUNIT:
filename += ".xml"
return Path(self.directory) / filename
model_config = {"use_enum_values": True}
class ExecutionConfig(BaseModel):
"""Test execution configuration"""
parallel_workers: PositiveInt = Field(default=4, description="Number of parallel workers")
max_concurrent_operations: PositiveInt = Field(default=10, description="Max concurrent operations per worker")
global_timeout: PositiveInt = Field(default=300, description="Global test timeout in seconds")
# Test filtering
include_tools: Optional[List[str]] = Field(None, description="Tools to include in testing")
exclude_tools: Optional[List[str]] = Field(None, description="Tools to exclude from testing")
include_tags: Optional[List[str]] = Field(None, description="Test tags to include")
exclude_tags: Optional[List[str]] = Field(None, description="Test tags to exclude")
name_pattern: Optional[str] = Field(None, description="Test name pattern filter")
# Execution options
fail_fast: bool = Field(default=False, description="Stop on first failure")
shuffle_tests: bool = Field(default=False, description="Randomize test execution order")
repeat_count: PositiveInt = Field(default=1, description="Number of times to repeat tests")
# Load balancing
distribute_by_server: bool = Field(default=True, description="Distribute tests across servers")
prefer_local_dependencies: bool = Field(default=True, description="Prefer running dependent tests on same worker")
@field_validator('include_tools', 'exclude_tools')
@classmethod
def validate_tool_lists(cls, v):
"""Validate tool filter lists"""
if v is not None:
# Remove empty strings and duplicates
return list(set(tool.strip() for tool in v if tool.strip()))
return v
@field_validator('name_pattern')
@classmethod
def validate_name_pattern(cls, v):
"""Validate regex pattern"""
if v:
try:
re.compile(v)
except re.error as e:
raise ValueError(f"Invalid regex pattern: {e}")
return v
class NotificationConfig(BaseModel):
"""Notification system configuration"""
enable_notifications: bool = Field(default=False, description="Enable notification testing")
notification_timeout: PositiveInt = Field(default=30, description="Notification timeout in seconds")
max_notifications_per_test: PositiveInt = Field(default=100, description="Maximum notifications per test")
buffer_size: PositiveInt = Field(default=1000, description="Notification buffer size")
# Notification types to test
test_resource_changes: bool = Field(default=True, description="Test resource list change notifications")
test_tool_changes: bool = Field(default=True, description="Test tool list change notifications")
test_prompt_changes: bool = Field(default=True, description="Test prompt list change notifications")
test_custom_notifications: bool = Field(default=False, description="Test custom notification types")
class GlobalConfig(BaseModel):
"""Global configuration container"""
# Feature flags
features: TestFeatures = Field(default_factory=TestFeatures, description="Feature configuration")
# Execution settings
execution: ExecutionConfig = Field(default_factory=ExecutionConfig, description="Execution configuration")
# Output settings
output: OutputConfig = Field(default_factory=OutputConfig, description="Output configuration")
# Retry policy
retry_policy: RetryPolicy = Field(default_factory=RetryPolicy, description="Retry policy")
# Notification settings
notifications: NotificationConfig = Field(default_factory=NotificationConfig, description="Notification configuration")
# Logging
log_level: LogLevel = Field(default=LogLevel.INFO, description="Global log level")
log_file: Optional[str] = Field(None, description="Log file path")
enable_debug_logging: bool = Field(default=False, description="Enable debug logging")
class TestConfig(BaseModel):
"""Main test configuration container"""
# Core configuration
servers: List[ServerConfig] = Field(..., min_length=1, description="Server configurations")
global_config: GlobalConfig = Field(default_factory=GlobalConfig, description="Global configuration")
# Test suites (populated by YAML parser)
test_suites: List[Any] = Field(default_factory=list, description="Test suites from YAML")
# Legacy fields for backward compatibility (non-conflicting names only)
include_tools: Optional[List[str]] = Field(None, description="Legacy: use global_config.execution.include_tools")
exclude_tools: Optional[List[str]] = Field(None, description="Legacy: use global_config.execution.exclude_tools")
features: Optional[Dict[str, Any]] = Field(None, description="Legacy: use global_config.features")
max_concurrent_operations: Optional[PositiveInt] = Field(None, description="Legacy: use global_config.execution.max_concurrent_operations")
enable_stress_testing: Optional[bool] = Field(None, description="Legacy: use global_config.features.enable_stress_testing")
enable_memory_profiling: Optional[bool] = Field(None, description="Legacy: use global_config.features.enable_memory_profiling")
enable_performance_profiling: Optional[bool] = Field(None, description="Legacy: use global_config.features.enable_performance_profiling")
global_timeout: Optional[PositiveInt] = Field(None, description="Legacy: use global_config.execution.global_timeout")
retry_policy: Optional[Dict[str, Any]] = Field(None, description="Legacy: use global_config.retry_policy")
notification_config: Optional[Dict[str, Any]] = Field(None, description="Legacy: use global_config.notifications")
@model_validator(mode='after')
def migrate_legacy_fields(self):
"""Migrate legacy fields to new global_config structure"""
# Migrate execution settings
if self.max_concurrent_operations is not None:
self.global_config.execution.max_concurrent_operations = self.max_concurrent_operations
if self.global_timeout is not None:
self.global_config.execution.global_timeout = self.global_timeout
if self.include_tools is not None:
self.global_config.execution.include_tools = self.include_tools
if self.exclude_tools is not None:
self.global_config.execution.exclude_tools = self.exclude_tools
# Migrate feature settings
if self.enable_stress_testing is not None:
self.global_config.features.enable_stress_testing = self.enable_stress_testing
if self.enable_memory_profiling is not None:
self.global_config.features.enable_memory_profiling = self.enable_memory_profiling
if self.enable_performance_profiling is not None:
self.global_config.features.enable_performance_profiling = self.enable_performance_profiling
# Migrate features dict
if self.features:
for key, value in self.features.items():
if hasattr(self.global_config.features, key):
setattr(self.global_config.features, key, value)
# Migrate retry policy
if self.retry_policy:
for key, value in self.retry_policy.items():
if hasattr(self.global_config.retry_policy, key):
setattr(self.global_config.retry_policy, key, value)
# Migrate notification config
if self.notification_config:
for key, value in self.notification_config.items():
if hasattr(self.global_config.notifications, key):
setattr(self.global_config.notifications, key, value)
return self
@classmethod
def from_cli_args(
cls,
server: str,
transport: str = "stdio",
timeout: int = 30,
auth_token: Optional[str] = None,
parallel: int = 4,
output: Optional[str] = None,
output_format: str = "console",
include_tools: Optional[List[str]] = None,
exclude_tools: Optional[List[str]] = None,
test_notifications: bool = False,
test_cancellation: bool = False,
test_progress: bool = False,
test_sampling: bool = False,
test_auth: bool = False,
max_concurrent: int = 10,
stress_test: bool = False,
memory_profile: bool = False,
performance_profile: bool = False,
**kwargs
) -> "TestConfig":
"""Create TestConfig from CLI arguments"""
# Create server config
server_config = ServerConfig(
name="cli_server",
command=server,
transport=transport,
timeout=timeout,
)
# Handle authentication
if auth_token:
server_config.auth.auth_type = AuthType.BEARER
server_config.auth.token = auth_token
# Create global config
global_config = GlobalConfig(
execution=ExecutionConfig(
parallel_workers=parallel,
max_concurrent_operations=max_concurrent,
include_tools=include_tools,
exclude_tools=exclude_tools,
),
output=OutputConfig(
format=OutputFormat(output_format),
directory=output,
),
features=TestFeatures(
test_notifications=test_notifications,
test_cancellation=test_cancellation,
test_progress=test_progress,
test_sampling=test_sampling,
test_authentication=test_auth,
enable_stress_testing=stress_test,
enable_memory_profiling=memory_profile,
enable_performance_profiling=performance_profile,
)
)
return cls(
servers=[server_config],
global_config=global_config,
)
def apply_filters(
self,
name_pattern: Optional[str] = None,
include_tags: Optional[List[str]] = None,
exclude_tags: Optional[List[str]] = None
) -> None:
"""Apply filters to test configuration"""
self.global_config.execution.name_pattern = name_pattern
if include_tags:
self.global_config.execution.include_tags = include_tags
if exclude_tags:
self.global_config.execution.exclude_tags = exclude_tags
def get_enabled_servers(self) -> List[ServerConfig]:
"""Get list of enabled servers"""
return [server for server in self.servers if server.enabled]
def get_server_by_name(self, name: str) -> Optional[ServerConfig]:
"""Get server configuration by name"""
for server in self.servers:
if server.name == name:
return server
return None
def validate_configuration(self) -> List[str]:
"""Validate entire configuration and return any issues"""
issues = []
# Validate servers
if not self.servers:
issues.append("No servers configured")
enabled_servers = self.get_enabled_servers()
if not enabled_servers:
issues.append("No enabled servers found")
# Validate server names are unique
server_names = [s.name for s in self.servers]
if len(server_names) != len(set(server_names)):
issues.append("Duplicate server names found")
# Validate output directory if specified
if self.global_config.output.directory:
try:
Path(self.global_config.output.directory).mkdir(parents=True, exist_ok=True)
except Exception as e:
issues.append(f"Cannot create output directory: {e}")
return issues
# Legacy property accessors for backward compatibility
@property
def parallel_workers(self) -> int:
return self.global_config.execution.parallel_workers
@parallel_workers.setter
def parallel_workers(self, value: int):
self.global_config.execution.parallel_workers = value
@property
def output_directory(self) -> Optional[str]:
return self.global_config.output.directory
@output_directory.setter
def output_directory(self, value: Optional[str]):
self.global_config.output.directory = value
@property
def output_format(self) -> OutputFormat:
return self.global_config.output.format
@output_format.setter
def output_format(self, value: Union[str, OutputFormat]):
if isinstance(value, str):
value = OutputFormat(value)
self.global_config.output.format = value
def has_failures(self) -> bool:
"""Check if configuration has any failures (placeholder for results)"""
# This method exists for CLI compatibility
# In practice, this would be called on test results, not config
return False
model_config = {
"use_enum_values": True,
"arbitrary_types_allowed": True, # For test_suites List[Any]
}
# Utility functions for configuration management
def load_config_from_env() -> Dict[str, Any]:
"""Load configuration from environment variables"""
config = {}
# Server configuration
if os.getenv('MCPTESTA_SERVER_COMMAND'):
config['server_command'] = os.getenv('MCPTESTA_SERVER_COMMAND')
if os.getenv('MCPTESTA_TRANSPORT'):
config['transport'] = os.getenv('MCPTESTA_TRANSPORT')
if os.getenv('MCPTESTA_AUTH_TOKEN'):
config['auth_token'] = os.getenv('MCPTESTA_AUTH_TOKEN')
# Execution configuration
if os.getenv('MCPTESTA_PARALLEL_WORKERS'):
config['parallel_workers'] = int(os.getenv('MCPTESTA_PARALLEL_WORKERS'))
if os.getenv('MCPTESTA_OUTPUT_DIR'):
config['output_directory'] = os.getenv('MCPTESTA_OUTPUT_DIR')
if os.getenv('MCPTESTA_OUTPUT_FORMAT'):
config['output_format'] = os.getenv('MCPTESTA_OUTPUT_FORMAT')
# Feature flags
if os.getenv('MCPTESTA_ENABLE_STRESS'):
config['enable_stress_testing'] = os.getenv('MCPTESTA_ENABLE_STRESS').lower() == 'true'
return config
def validate_config_compatibility(config: TestConfig) -> List[str]:
"""Validate configuration compatibility across different components"""
issues = []
# Check transport compatibility with authentication
for server in config.servers:
if server.transport == TransportType.STDIO and server.auth.auth_type != AuthType.NONE:
issues.append(f"Server '{server.name}': Authentication not supported with stdio transport")
if server.transport in [TransportType.SSE, TransportType.WS]:
if not server.command.startswith(('http://', 'https://')):
issues.append(f"Server '{server.name}': {server.transport} transport requires HTTP(S) URL")
# Check feature compatibility
if config.global_config.features.test_notifications and not any(
s.transport in [TransportType.SSE, TransportType.WS] for s in config.servers
):
issues.append("Notification testing requires SSE or WebSocket transport")
return issues

View File

@ -0,0 +1,768 @@
"""
MCPTesta Test Session Management
Manages test session lifecycle, state tracking, resource management,
connection pooling, and session-scoped metrics and logging.
"""
import asyncio
import time
import uuid
from datetime import datetime, timedelta
from typing import Dict, Any, List, Optional, Set, AsyncIterator, Callable
from dataclasses import dataclass, field
from contextlib import asynccontextmanager
from collections import defaultdict, deque
import weakref
from .config import TestConfig, ServerConfig
from .client import MCPTestClient, TestResult, ServerCapabilities
from ..utils.logging import get_logger, LogContext, session_logging_context
from ..utils.metrics import MetricsCollector, metrics_session, MetricsContext
@dataclass
class SessionMetrics:
"""Session-level metrics tracking"""
session_id: str
start_time: datetime = field(default_factory=datetime.now)
end_time: Optional[datetime] = None
# Connection metrics
total_connections: int = 0
successful_connections: int = 0
failed_connections: int = 0
connection_pool_hits: int = 0
connection_pool_misses: int = 0
# Test execution metrics
total_tests: int = 0
passed_tests: int = 0
failed_tests: int = 0
skipped_tests: int = 0
cancelled_tests: int = 0
# Performance metrics
total_execution_time: float = 0.0
average_test_time: float = 0.0
peak_memory_usage: float = 0.0
peak_concurrent_operations: int = 0
# Resource utilization
server_utilization: Dict[str, float] = field(default_factory=dict)
worker_efficiency: float = 0.0
@property
def duration(self) -> Optional[timedelta]:
"""Get session duration"""
if self.end_time:
return self.end_time - self.start_time
return datetime.now() - self.start_time
@property
def success_rate(self) -> float:
"""Calculate test success rate"""
if self.total_tests == 0:
return 0.0
return self.passed_tests / self.total_tests * 100
@property
def connection_success_rate(self) -> float:
"""Calculate connection success rate"""
if self.total_connections == 0:
return 0.0
return self.successful_connections / self.total_connections * 100
@dataclass
class SessionState:
"""Current state of test session"""
phase: str = "initializing" # initializing, connecting, executing, cleanup, completed
current_suite: Optional[str] = None
current_test: Optional[str] = None
active_operations: Set[str] = field(default_factory=set)
failed_operations: Set[str] = field(default_factory=set)
cancelled_operations: Set[str] = field(default_factory=set)
# Progress tracking
total_planned_tests: int = 0
completed_tests: int = 0
@property
def progress_percentage(self) -> float:
"""Calculate completion percentage"""
if self.total_planned_tests == 0:
return 0.0
return (self.completed_tests / self.total_planned_tests) * 100
class ConnectionPool:
"""Connection pool for efficient server connection management"""
def __init__(self, max_size: int = 10, idle_timeout: float = 300.0):
self.max_size = max_size
self.idle_timeout = idle_timeout
self._pools: Dict[str, deque] = defaultdict(deque)
self._in_use: Dict[str, Set] = defaultdict(set)
self._last_used: Dict[str, Dict[MCPTestClient, float]] = defaultdict(dict)
self._lock = asyncio.Lock()
self._cleanup_task: Optional[asyncio.Task] = None
self.logger = get_logger(__name__)
async def start(self):
"""Start the connection pool"""
if not self._cleanup_task:
self._cleanup_task = asyncio.create_task(self._cleanup_idle_connections())
async def stop(self):
"""Stop the connection pool and cleanup all connections"""
if self._cleanup_task:
self._cleanup_task.cancel()
try:
await self._cleanup_task
except asyncio.CancelledError:
pass
async with self._lock:
for server_key, pool in self._pools.items():
while pool:
client = pool.popleft()
try:
await client._close_connection()
except Exception as e:
self.logger.warning(f"Error closing pooled connection: {e}")
for server_key, in_use_set in self._in_use.items():
for client in in_use_set.copy():
try:
await client._close_connection()
except Exception as e:
self.logger.warning(f"Error closing active connection: {e}")
@asynccontextmanager
async def get_connection(self, server_config: ServerConfig) -> AsyncIterator[MCPTestClient]:
"""Get a connection from the pool or create a new one"""
server_key = self._get_server_key(server_config)
client = None
try:
async with self._lock:
# Try to get from pool
if self._pools[server_key]:
client = self._pools[server_key].popleft()
self._in_use[server_key].add(client)
if client in self._last_used[server_key]:
del self._last_used[server_key][client]
self.logger.debug(f"Reusing pooled connection for {server_key}")
else:
# Create new connection with shared metrics collector if available
metrics_collector = None
# Try to get metrics collector from current context
try:
from ..utils.metrics import get_global_metrics
metrics_collector = get_global_metrics()
except:
pass
client = MCPTestClient(server_config, metrics_collector=metrics_collector)
self._in_use[server_key].add(client)
self.logger.debug(f"Creating new connection for {server_key}")
# Connect if not already connected
if not client.is_connected:
await client._establish_connection()
yield client
finally:
if client:
async with self._lock:
if client in self._in_use[server_key]:
self._in_use[server_key].remove(client)
# Return to pool if healthy and not at capacity
if (client.is_connected and
len(self._pools[server_key]) < self.max_size):
self._pools[server_key].append(client)
self._last_used[server_key][client] = time.time()
self.logger.debug(f"Returned connection to pool for {server_key}")
else:
# Close connection
try:
await client._close_connection()
self.logger.debug(f"Closed excess connection for {server_key}")
except Exception as e:
self.logger.warning(f"Error closing connection: {e}")
def _get_server_key(self, server_config: ServerConfig) -> str:
"""Generate a unique key for server configuration"""
return f"{server_config.transport}:{server_config.command}:{server_config.auth_token}"
async def _cleanup_idle_connections(self):
"""Cleanup idle connections periodically"""
while True:
try:
await asyncio.sleep(60) # Check every minute
current_time = time.time()
async with self._lock:
for server_key, pool in self._pools.items():
last_used_map = self._last_used[server_key]
to_remove = []
for client in list(pool):
if client in last_used_map:
idle_time = current_time - last_used_map[client]
if idle_time > self.idle_timeout:
to_remove.append(client)
for client in to_remove:
pool.remove(client)
del last_used_map[client]
try:
await client._close_connection()
self.logger.debug(f"Closed idle connection for {server_key}")
except Exception as e:
self.logger.warning(f"Error closing idle connection: {e}")
except asyncio.CancelledError:
break
except Exception as e:
self.logger.error(f"Error in connection cleanup: {e}")
class TestSession:
"""
Manages the lifecycle of a test session with comprehensive state tracking,
resource management, and metrics collection.
"""
def __init__(self, config: TestConfig, session_id: Optional[str] = None):
self.config = config
self.session_id = session_id or str(uuid.uuid4())
self.logger = get_logger(f"{__name__}.{self.session_id}")
# Session state and metrics
self.metrics = SessionMetrics(session_id=self.session_id)
self.state = SessionState()
self.metrics_collector = MetricsCollector()
# Connection management
pool_size = max(config.global_config.execution.parallel_workers * 2, 10)
self.connection_pool = ConnectionPool(max_size=pool_size)
# Test results and execution tracking
self.test_results: List[TestResult] = []
self.server_capabilities: Dict[str, ServerCapabilities] = {}
self.execution_history: List[Dict[str, Any]] = []
# Event handlers and callbacks
self._event_handlers: Dict[str, List[Callable]] = defaultdict(list)
self._cleanup_callbacks: List[Callable] = []
# Resource tracking
self._active_clients: weakref.WeakSet = weakref.WeakSet()
self._resource_usage: Dict[str, Any] = {}
# Session lifecycle state
self._started = False
self._completed = False
self._cancelled = False
async def __aenter__(self):
"""Async context manager entry"""
await self.start()
return self
async def __aexit__(self, exc_type, exc_val, exc_tb):
"""Async context manager exit"""
await self.cleanup()
async def start(self):
"""Start the test session"""
if self._started:
return
self.logger.info(f"Starting test session {self.session_id}")
self.state.phase = "connecting"
self._started = True
# Start metrics monitoring
await self.metrics_collector.start_monitoring()
# Start connection pool
await self.connection_pool.start()
# Calculate total planned tests
total_tests = 0
for suite in self.config.test_suites:
# Handle both old dict format and new TestSuite objects
if hasattr(suite, 'tests'):
# New TestSuite object format
total_tests += len([test for test in suite.tests if getattr(test, 'enabled', True)])
else:
# Legacy dict format
total_tests += len([test for test in suite.get('tests', []) if test.get('enabled', True)])
self.state.total_planned_tests = total_tests
# Set logging context
with LogContext(session_id=self.session_id):
# Emit session started event
await self._emit_event("session_started", {
"session_id": self.session_id,
"config": self.config,
"planned_tests": total_tests
})
self.logger.info(f"Session started with {total_tests} planned tests")
async def cleanup(self):
"""Cleanup session resources"""
if self._completed:
return
self.logger.info(f"Cleaning up session {self.session_id}")
self.state.phase = "cleanup"
try:
# Run cleanup callbacks
for callback in self._cleanup_callbacks:
try:
if asyncio.iscoroutinefunction(callback):
await callback()
else:
callback()
except Exception as e:
self.logger.warning(f"Error in cleanup callback: {e}")
# Stop metrics monitoring
await self.metrics_collector.stop_monitoring()
# Stop connection pool
await self.connection_pool.stop()
# Finalize metrics
self.metrics.end_time = datetime.now()
if self.test_results:
self.metrics.average_test_time = (
sum(r.execution_time for r in self.test_results) / len(self.test_results)
)
# Update metrics collector with final session stats
session_summary = self.get_session_summary()
for key, value in session_summary['metrics'].items():
if isinstance(value, (int, float)):
self.metrics_collector.record_metric(f'session_{key}', value, {
'session_id': self.session_id
})
# Set final logging context
with LogContext(session_id=self.session_id):
# Emit session completed event
await self._emit_event("session_completed", {
"session_id": self.session_id,
"metrics": self.metrics,
"results": self.test_results,
"summary": session_summary
})
self.state.phase = "completed"
self._completed = True
self.logger.info(f"Session cleanup completed - {len(self.test_results)} tests executed")
except Exception as e:
self.logger.error(f"Error during session cleanup: {e}")
raise
async def get_client(self, server_config: ServerConfig) -> AsyncIterator[MCPTestClient]:
"""Get a test client for the specified server with enhanced tracking"""
start_time = time.time()
try:
async with self.connection_pool.get_connection(server_config) as client:
connection_time = time.time() - start_time
# Track client and update metrics
self._active_clients.add(client)
self.metrics.total_connections += 1
# Record connection performance
if client.is_connected:
self.metrics.successful_connections += 1
self.metrics_collector.record_connection_performance(
server_config.name,
server_config.transport,
connection_time,
success=True
)
else:
self.metrics.failed_connections += 1
self.metrics_collector.record_connection_performance(
server_config.name,
server_config.transport,
connection_time,
success=False
)
# Update active connection count for resource tracking
active_count = len(self._active_clients)
self.metrics_collector.resource_metrics['active_connections'] = active_count
self.metrics_collector.resource_metrics['peak_connections'] = max(
self.metrics_collector.resource_metrics['peak_connections'],
active_count
)
with LogContext(server_name=server_config.name, session_id=self.session_id):
yield client
except Exception as e:
connection_time = time.time() - start_time
self.metrics.failed_connections += 1
self.metrics_collector.record_connection_performance(
server_config.name,
server_config.transport,
connection_time,
success=False
)
self.logger.error(f"Failed to get client for {server_config.name}: {e}")
raise
def add_test_result(self, result: TestResult):
"""Add a test result to the session with enhanced tracking"""
self.test_results.append(result)
self.state.completed_tests += 1
# Update session metrics
self.metrics.total_tests += 1
if result.success:
self.metrics.passed_tests += 1
else:
self.metrics.failed_tests += 1
self.metrics.total_execution_time += result.execution_time
# Update metrics collector
self.metrics_collector.record_test_result(
execution_time=result.execution_time,
success=result.success,
skipped=result.metadata.get('skipped', False) if result.metadata else False
)
# Track operation state
if hasattr(result, 'metadata') and result.metadata:
operation_id = result.metadata.get('operation_id')
if operation_id:
if result.success:
self.state.active_operations.discard(operation_id)
else:
self.state.failed_operations.add(operation_id)
# Record test type specific metrics
test_type = result.metadata.get('test_type')
if test_type == 'tool_call':
tool_name = result.metadata.get('tool_name')
if tool_name:
error_type = None if result.success else type(result.error_message).__name__ if result.error_message else 'UnknownError'
self.metrics_collector.record_tool_call(
tool_name, result.execution_time, result.success, error_type
)
elif test_type == 'resource_read':
resource_uri = result.metadata.get('resource_uri')
if resource_uri:
self.metrics_collector.record_resource_read(
resource_uri, result.execution_time, result.success
)
elif test_type == 'prompt_get':
prompt_name = result.metadata.get('prompt_name')
if prompt_name:
self.metrics_collector.record_prompt_get(
prompt_name, result.execution_time, result.success
)
# Log with proper context
with LogContext(test_name=result.test_name, session_id=self.session_id):
self.logger.info(f"Test result: {result.test_name} ({'PASS' if result.success else 'FAIL'}) "
f"in {result.execution_time:.3f}s")
# Update progress and emit progress event if significant milestone
progress = self.state.progress_percentage
if progress > 0 and progress % 10 == 0: # Every 10% completion
asyncio.create_task(self._emit_event("progress_update", {
"session_id": self.session_id,
"progress": progress,
"completed": self.state.completed_tests,
"total": self.state.total_planned_tests
}))
def update_current_test(self, suite_name: str, test_name: str):
"""Update the current test being executed"""
self.state.current_suite = suite_name
self.state.current_test = test_name
self.logger.debug(f"Current test: {suite_name}.{test_name}")
def add_server_capabilities(self, server_name: str, capabilities: ServerCapabilities):
"""Add discovered server capabilities"""
self.server_capabilities[server_name] = capabilities
self.logger.info(f"Server capabilities added for {server_name}: "
f"{len(capabilities.tools)} tools, {len(capabilities.resources)} resources")
async def cancel_session(self, reason: str = "User requested"):
"""Cancel the entire session"""
if self._cancelled:
return
self.logger.warning(f"Cancelling session: {reason}")
self._cancelled = True
self.state.phase = "cancelled"
# Cancel active operations
for operation_id in self.state.active_operations.copy():
self.state.cancelled_operations.add(operation_id)
self.state.active_operations.discard(operation_id)
await self._emit_event("session_cancelled", {
"session_id": self.session_id,
"reason": reason
})
def is_cancelled(self) -> bool:
"""Check if session is cancelled"""
return self._cancelled
def register_cleanup_callback(self, callback: Callable):
"""Register a cleanup callback"""
self._cleanup_callbacks.append(callback)
def register_event_handler(self, event_type: str, handler: Callable):
"""Register an event handler"""
self._event_handlers[event_type].append(handler)
async def _emit_event(self, event_type: str, event_data: Dict[str, Any]):
"""Emit an event to registered handlers"""
handlers = self._event_handlers.get(event_type, [])
for handler in handlers:
try:
if asyncio.iscoroutinefunction(handler):
await handler(event_data)
else:
handler(event_data)
except Exception as e:
self.logger.warning(f"Error in event handler for {event_type}: {e}")
def get_session_summary(self) -> Dict[str, Any]:
"""Get comprehensive session summary"""
return {
"session_id": self.session_id,
"metrics": {
"duration": str(self.metrics.duration) if self.metrics.duration else None,
"total_tests": self.metrics.total_tests,
"success_rate": self.metrics.success_rate,
"connection_success_rate": self.metrics.connection_success_rate,
"average_test_time": self.metrics.average_test_time,
"total_execution_time": self.metrics.total_execution_time,
},
"state": {
"phase": self.state.phase,
"progress": self.state.progress_percentage,
"current_suite": self.state.current_suite,
"current_test": self.state.current_test,
"active_operations": len(self.state.active_operations),
"failed_operations": len(self.state.failed_operations),
},
"servers": {
name: {
"tools": len(caps.tools),
"resources": len(caps.resources),
"prompts": len(caps.prompts),
"supports_notifications": caps.supports_notifications,
"supports_cancellation": caps.supports_cancellation,
}
for name, caps in self.server_capabilities.items()
},
"results": {
"total": len(self.test_results),
"passed": len([r for r in self.test_results if r.success]),
"failed": len([r for r in self.test_results if not r.success]),
}
}
def export_results(self, format: str = "dict") -> Any:
"""Export session results in specified format"""
summary = self.get_session_summary()
if format == "dict":
return summary
elif format == "json":
import json
return json.dumps(summary, indent=2, default=str)
elif format == "yaml":
import yaml
return yaml.dump(summary, default_flow_style=False)
else:
raise ValueError(f"Unsupported export format: {format}")
async def execute_test_with_context(self, test_case, server_config: ServerConfig,
suite_name: str = "default"):
"""Execute a single test with full session context and metrics tracking"""
# Update current test context
self.update_current_test(suite_name, test_case.name)
# Create metrics context for this test
with MetricsContext(f"test_{test_case.name}", self.metrics_collector):
with LogContext(
test_name=test_case.name,
test_type=test_case.test_type,
session_id=self.session_id,
server_name=server_config.name
):
try:
# Track operation start
operation_id = str(uuid.uuid4())
self.state.active_operations.add(operation_id)
# Get client and execute test
async with self.get_client(server_config) as client:
start_time = time.time()
# Execute test based on type
if test_case.test_type == "tool_call":
result = await client.call_tool(
tool_name=test_case.target,
parameters=test_case.parameters or {},
timeout=test_case.timeout,
enable_cancellation=getattr(test_case, 'enable_cancellation', False),
enable_progress=getattr(test_case, 'enable_progress', False),
enable_sampling=getattr(test_case, 'enable_sampling', False),
sampling_rate=getattr(test_case, 'sampling_rate', 1.0)
)
elif test_case.test_type == "resource_read":
result = await client.read_resource(
resource_uri=test_case.target,
timeout=test_case.timeout
)
elif test_case.test_type == "prompt_get":
result = await client.get_prompt(
prompt_name=test_case.target,
arguments=test_case.parameters or {},
timeout=test_case.timeout
)
elif test_case.test_type == "ping":
result = await client.ping(timeout=test_case.timeout)
else:
raise ValueError(f"Unsupported test type: {test_case.test_type}")
# Enhance result with session context
if result.metadata is None:
result.metadata = {}
result.metadata.update({
'operation_id': operation_id,
'test_type': test_case.test_type,
'session_id': self.session_id,
'suite_name': suite_name,
'server_name': server_config.name
})
# Add test result to session
self.add_test_result(result)
return result
except Exception as e:
# Create failed result
execution_time = time.time() - start_time if 'start_time' in locals() else 0.0
result = TestResult(
test_name=test_case.name,
success=False,
execution_time=execution_time,
error_message=str(e),
metadata={
'operation_id': operation_id,
'test_type': test_case.test_type,
'session_id': self.session_id,
'suite_name': suite_name,
'server_name': server_config.name,
'exception_type': type(e).__name__
}
)
self.add_test_result(result)
self.logger.error(f"Test {test_case.name} failed: {e}")
return result
finally:
# Clean up operation tracking
self.state.active_operations.discard(operation_id)
def get_performance_summary(self) -> Dict[str, Any]:
"""Get comprehensive performance summary from metrics collector"""
return self.metrics_collector.get_summary_stats()
def get_resource_usage_timeline(self, minutes: int = 30) -> List[Dict[str, Any]]:
"""Get resource usage timeline for analysis"""
since = datetime.now() - timedelta(minutes=minutes)
timeline = []
for snapshot in self.metrics_collector.resource_snapshots:
if snapshot.timestamp >= since:
timeline.append({
'timestamp': snapshot.timestamp.isoformat(),
'memory_mb': snapshot.memory_mb,
'memory_percent': snapshot.memory_percent,
'cpu_percent': snapshot.cpu_percent,
'active_connections': snapshot.active_connections,
'active_threads': snapshot.active_threads
})
return timeline
async def pause_session(self, reason: str = "User requested"):
"""Pause the session temporarily"""
if self.state.phase in ["completed", "cancelled"]:
return
self.logger.info(f"Pausing session: {reason}")
previous_phase = self.state.phase
self.state.phase = "paused"
await self._emit_event("session_paused", {
"session_id": self.session_id,
"reason": reason,
"previous_phase": previous_phase
})
async def resume_session(self):
"""Resume a paused session"""
if self.state.phase != "paused":
return
self.logger.info("Resuming session")
self.state.phase = "executing"
await self._emit_event("session_resumed", {
"session_id": self.session_id
})
def is_healthy(self) -> bool:
"""Check if session is in a healthy state"""
if self._cancelled or self.state.phase in ["cancelled", "error"]:
return False
# Check if too many failures
if self.metrics.total_tests > 0:
failure_rate = self.metrics.failed_tests / self.metrics.total_tests
if failure_rate > 0.8: # More than 80% failures
return False
# Check if system resources are critically low
current_memory = self.metrics_collector.resource_metrics.get('current_memory_mb', 0)
if current_memory > 0:
memory_percent = self.metrics_collector.resource_metrics.get('peak_memory_percent', 0)
if memory_percent > 95: # More than 95% memory usage
return False
return True

View File

@ -0,0 +1,13 @@
"""
MCPTesta Protocol Components
MCP protocol feature testing and connectivity utilities.
"""
from .features import ProtocolFeatures
from .ping import PingTester
__all__ = [
"ProtocolFeatures",
"PingTester",
]

View File

@ -0,0 +1,420 @@
"""
MCP Protocol Advanced Features Testing
Comprehensive testing support for advanced MCP protocol features including
notifications, cancellation, progress reporting, sampling, and authentication.
"""
import asyncio
import time
import uuid
from typing import Dict, Any, List, Optional, Callable, Union
from dataclasses import dataclass, field
from enum import Enum
from fastmcp.client import Client
from ..utils.logging import get_logger
class NotificationType(Enum):
"""Supported notification types"""
RESOURCES_LIST_CHANGED = "notifications/resources/list_changed"
TOOLS_LIST_CHANGED = "notifications/tools/list_changed"
PROMPTS_LIST_CHANGED = "notifications/prompts/list_changed"
PROGRESS = "notifications/progress"
CANCELLED = "notifications/cancelled"
CUSTOM = "custom"
@dataclass
class NotificationEvent:
"""Notification event data"""
notification_type: str
method: str
params: Dict[str, Any]
timestamp: float = field(default_factory=time.time)
client_id: Optional[str] = None
@dataclass
class ProgressUpdate:
"""Progress update information"""
progress_token: str
progress: float # 0.0 to 1.0
total: Optional[int] = None
completed: Optional[int] = None
message: Optional[str] = None
timestamp: float = field(default_factory=time.time)
@dataclass
class CancellationRequest:
"""Cancellation request information"""
request_id: str
method: str
reason: Optional[str] = None
timestamp: float = field(default_factory=time.time)
class ProtocolFeatures:
"""
Advanced MCP protocol features tester.
Provides comprehensive testing for:
- Notification system (list changes, progress, custom notifications)
- Request cancellation and cleanup
- Progress reporting and streaming updates
- Sampling and throttling mechanisms
- Authentication and session management
"""
def __init__(self):
self.logger = get_logger(__name__)
self._notification_handlers: Dict[str, List[Callable]] = {}
self._progress_handlers: Dict[str, Callable] = {}
self._cancellation_handlers: Dict[str, Callable] = {}
# Notification state tracking
self._received_notifications: List[NotificationEvent] = []
self._notification_lock = asyncio.Lock()
async def test_notifications(self, client: Client) -> bool:
"""Test notification system support"""
try:
self.logger.debug("Testing notification system support")
# Register for notifications if supported
notification_types = [
NotificationType.RESOURCES_LIST_CHANGED.value,
NotificationType.TOOLS_LIST_CHANGED.value,
NotificationType.PROMPTS_LIST_CHANGED.value,
]
for notification_type in notification_types:
try:
# Attempt to subscribe to notifications
await self._subscribe_to_notifications(client, notification_type)
return True
except Exception:
continue
return False
except Exception as e:
self.logger.debug(f"Notification system not supported: {e}")
return False
async def test_cancellation(self, client: Client) -> bool:
"""Test request cancellation support"""
try:
self.logger.debug("Testing cancellation support")
# Start a long-running operation to test cancellation
request_id = str(uuid.uuid4())
# Create a test operation (list_tools is usually quick, but we'll simulate)
operation_task = asyncio.create_task(
self._simulate_long_operation(client, request_id)
)
# Wait briefly then attempt cancellation
await asyncio.sleep(0.1)
cancellation_success = await self._send_cancellation_request(client, request_id)
# Cancel the task
operation_task.cancel()
return cancellation_success
except Exception as e:
self.logger.debug(f"Cancellation not supported: {e}")
return False
async def test_progress(self, client: Client) -> bool:
"""Test progress reporting support"""
try:
self.logger.debug("Testing progress reporting support")
# Look for progress-enabled operations
capabilities = await client.list_tools()
# Check if any tools support progress
for tool in capabilities.get("tools", []):
if tool.get("supports_progress", False):
return True
# Test generic progress support
return await self._test_generic_progress(client)
except Exception as e:
self.logger.debug(f"Progress reporting not supported: {e}")
return False
async def test_sampling(self, client: Client) -> bool:
"""Test sampling/throttling support"""
try:
self.logger.debug("Testing sampling support")
# Test if client accepts sampling parameters
capabilities = await client.list_tools()
# Look for sampling support indicators
for tool in capabilities.get("tools", []):
if any(param.get("name") == "sampling_rate" for param in tool.get("inputSchema", {}).get("properties", {}).values()):
return True
return False
except Exception as e:
self.logger.debug(f"Sampling not supported: {e}")
return False
async def _subscribe_to_notifications(self, client: Client, notification_type: str):
"""Subscribe to specific notification type"""
# Implementation depends on FastMCP notification support
# For now, we'll simulate subscription
self.logger.debug(f"Subscribing to notifications: {notification_type}")
# Register local handler
self._notification_handlers.setdefault(notification_type, []).append(
self._default_notification_handler
)
async def _send_cancellation_request(self, client: Client, request_id: str) -> bool:
"""Send cancellation request for operation"""
try:
# Implementation depends on FastMCP cancellation support
# For now, simulate cancellation
cancellation = CancellationRequest(
request_id=request_id,
method="test_operation",
reason="Testing cancellation feature"
)
self.logger.debug(f"Sending cancellation request: {request_id}")
return True
except Exception:
return False
async def _simulate_long_operation(self, client: Client, request_id: str):
"""Simulate a long-running operation for cancellation testing"""
try:
# Simulate work
for i in range(10):
await asyncio.sleep(0.1)
# Send progress update if supported
await self._send_progress_update(request_id, i / 10.0, f"Step {i+1}/10")
except asyncio.CancelledError:
self.logger.debug(f"Operation {request_id} was cancelled")
raise
async def _test_generic_progress(self, client: Client) -> bool:
"""Test generic progress reporting capability"""
try:
# Test if we can send progress updates
progress_token = str(uuid.uuid4())
await self._send_progress_update(progress_token, 0.5, "Testing progress")
return True
except Exception:
return False
async def _send_progress_update(self, progress_token: str, progress: float, message: str = None):
"""Send progress update"""
progress_update = ProgressUpdate(
progress_token=progress_token,
progress=progress,
message=message
)
# Store progress update for handlers
if progress_token in self._progress_handlers:
handler = self._progress_handlers[progress_token]
await handler(progress_update)
self.logger.debug(f"Progress update: {progress_token} -> {progress:.1%}")
def _default_notification_handler(self, notification: NotificationEvent):
"""Default notification handler"""
asyncio.create_task(self._store_notification(notification))
async def _store_notification(self, notification: NotificationEvent):
"""Store received notification"""
async with self._notification_lock:
self._received_notifications.append(notification)
self.logger.debug(f"Received notification: {notification.notification_type}")
# Public API for test registration
def register_notification_handler(self,
notification_type: str,
handler: Callable[[NotificationEvent], None]):
"""Register handler for specific notification type"""
self._notification_handlers.setdefault(notification_type, []).append(handler)
def register_progress_handler(self,
progress_token: str,
handler: Callable[[ProgressUpdate], None]):
"""Register handler for progress updates"""
self._progress_handlers[progress_token] = handler
def register_cancellation_handler(self,
request_id: str,
handler: Callable[[CancellationRequest], None]):
"""Register handler for cancellation requests"""
self._cancellation_handlers[request_id] = handler
# Test scenarios for comprehensive notification testing
async def test_resource_list_change_notification(self, client: Client) -> Dict[str, Any]:
"""Test resource list change notifications"""
test_results = {
"notification_type": NotificationType.RESOURCES_LIST_CHANGED.value,
"subscribed": False,
"notifications_received": 0,
"test_duration": 0.0
}
start_time = time.time()
try:
# Subscribe to resource list changes
await self._subscribe_to_notifications(
client,
NotificationType.RESOURCES_LIST_CHANGED.value
)
test_results["subscribed"] = True
# Trigger resource list change (if possible)
initial_count = len(self._received_notifications)
# Wait for potential notifications
await asyncio.sleep(2.0)
final_count = len(self._received_notifications)
test_results["notifications_received"] = final_count - initial_count
except Exception as e:
test_results["error"] = str(e)
test_results["test_duration"] = time.time() - start_time
return test_results
async def test_tools_list_change_notification(self, client: Client) -> Dict[str, Any]:
"""Test tools list change notifications"""
test_results = {
"notification_type": NotificationType.TOOLS_LIST_CHANGED.value,
"subscribed": False,
"notifications_received": 0,
"test_duration": 0.0
}
start_time = time.time()
try:
# Subscribe to tools list changes
await self._subscribe_to_notifications(
client,
NotificationType.TOOLS_LIST_CHANGED.value
)
test_results["subscribed"] = True
# Trigger tools list change (if possible)
initial_count = len(self._received_notifications)
# Wait for potential notifications
await asyncio.sleep(2.0)
final_count = len(self._received_notifications)
test_results["notifications_received"] = final_count - initial_count
except Exception as e:
test_results["error"] = str(e)
test_results["test_duration"] = time.time() - start_time
return test_results
async def test_custom_notification(self,
client: Client,
notification_type: str,
payload: Dict[str, Any] = None) -> Dict[str, Any]:
"""Test custom notification type"""
test_results = {
"notification_type": notification_type,
"subscribed": False,
"payload_sent": payload,
"notifications_received": 0,
"test_duration": 0.0
}
start_time = time.time()
try:
# Subscribe to custom notification
await self._subscribe_to_notifications(client, notification_type)
test_results["subscribed"] = True
# Send custom notification if supported
if payload:
await self._send_custom_notification(client, notification_type, payload)
# Wait for potential notifications
await asyncio.sleep(1.0)
test_results["notifications_received"] = len(self._received_notifications)
except Exception as e:
test_results["error"] = str(e)
test_results["test_duration"] = time.time() - start_time
return test_results
async def _send_custom_notification(self,
client: Client,
notification_type: str,
payload: Dict[str, Any]):
"""Send custom notification"""
# Implementation depends on FastMCP custom notification support
self.logger.debug(f"Sending custom notification: {notification_type} with payload: {payload}")
# Utility methods
def get_received_notifications(self, notification_type: Optional[str] = None) -> List[NotificationEvent]:
"""Get received notifications, optionally filtered by type"""
if notification_type:
return [n for n in self._received_notifications if n.notification_type == notification_type]
return self._received_notifications.copy()
def clear_received_notifications(self):
"""Clear received notifications list"""
self._received_notifications.clear()
@property
def notification_count(self) -> int:
"""Get total number of received notifications"""
return len(self._received_notifications)

View File

@ -0,0 +1,597 @@
"""
MCPTesta Ping Testing Utilities
Comprehensive ping testing system for FastMCP servers with connectivity tests,
latency measurement, reliability testing, and timeout/retry logic.
"""
import asyncio
import time
import statistics
from typing import Dict, Any, List, Optional, Tuple
from dataclasses import dataclass, field
from datetime import datetime, timedelta
from contextlib import asynccontextmanager
from ..core.client import MCPTestClient, TestResult
from ..utils.logging import get_logger
from ..utils.metrics import MetricsCollector
@dataclass
class PingResult:
"""Result of a single ping test"""
sequence: int
success: bool
latency_ms: float
timestamp: datetime
error_message: Optional[str] = None
server_response: Optional[Any] = None
metadata: Dict[str, Any] = field(default_factory=dict)
@dataclass
class PingStatistics:
"""Statistics for a series of ping tests"""
total_pings: int
successful_pings: int
failed_pings: int
packet_loss_percent: float
min_latency_ms: float
max_latency_ms: float
avg_latency_ms: float
median_latency_ms: float
stddev_latency_ms: float
jitter_ms: float
uptime_percent: float
total_duration_seconds: float
error_breakdown: Dict[str, int] = field(default_factory=dict)
@property
def success_rate(self) -> float:
"""Calculate success rate percentage"""
if self.total_pings == 0:
return 0.0
return (self.successful_pings / self.total_pings) * 100
@dataclass
class ReliabilityTestResult:
"""Result of reliability testing"""
test_duration_minutes: float
ping_statistics: PingStatistics
connection_drops: int
recovery_times: List[float]
average_recovery_time: float
longest_outage_seconds: float
availability_percent: float
stability_score: float # 0-10 stability rating
class PingTester:
"""
Comprehensive ping testing system for FastMCP servers.
Features:
- Basic connectivity testing with configurable timeouts
- Latency measurement and statistical analysis
- Connection reliability and stability testing
- Retry logic with exponential backoff
- Performance trend analysis
- Real-time monitoring capabilities
"""
def __init__(self,
server_config: Any,
enable_metrics: bool = True,
enable_logging: bool = True):
self.server_config = server_config
self.logger = get_logger(__name__) if enable_logging else None
self.metrics = MetricsCollector() if enable_metrics else None
# Test configuration defaults
self.default_timeout = 5.0
self.default_retry_count = 3
self.default_retry_delay = 1.0
self.default_ping_method = "list_tools" # lightweight operation
async def ping_once(self,
timeout: float = None,
sequence: int = 1,
ping_method: str = None) -> PingResult:
"""Perform single ping test"""
if timeout is None:
timeout = self.default_timeout
if ping_method is None:
ping_method = self.default_ping_method
start_time = time.time()
timestamp = datetime.now()
try:
if self.logger:
self.logger.debug(f"Ping #{sequence} to {self.server_config.name} using {ping_method}")
# Create test client for this ping
test_client = MCPTestClient(self.server_config, enable_metrics=False, enable_logging=False)
async with test_client.connect():
# Perform ping operation based on method
if ping_method == "list_tools":
result = await test_client._client.list_tools()
elif ping_method == "server_info":
result = await test_client._client.get_server_info()
elif ping_method == "capabilities":
# Test basic capability discovery
await test_client._discover_capabilities()
result = {"capabilities": "discovered"}
else:
raise ValueError(f"Unknown ping method: {ping_method}")
latency = (time.time() - start_time) * 1000 # Convert to milliseconds
# Record successful ping
if self.metrics:
self.metrics.record_test_completion(
test_name=f"ping_{sequence}",
test_type="ping",
start_time=start_time,
success=True,
server_name=self.server_config.name,
metadata={"latency_ms": latency, "ping_method": ping_method}
)
return PingResult(
sequence=sequence,
success=True,
latency_ms=latency,
timestamp=timestamp,
server_response=result,
metadata={"ping_method": ping_method}
)
except asyncio.TimeoutError:
latency = (time.time() - start_time) * 1000
error_msg = f"Ping timeout after {timeout}s"
if self.metrics:
self.metrics.record_test_completion(
test_name=f"ping_{sequence}",
test_type="ping",
start_time=start_time,
success=False,
server_name=self.server_config.name,
error_type="TimeoutError",
metadata={"timeout_ms": timeout * 1000}
)
return PingResult(
sequence=sequence,
success=False,
latency_ms=latency,
timestamp=timestamp,
error_message=error_msg,
metadata={"timeout": True}
)
except Exception as e:
latency = (time.time() - start_time) * 1000
error_msg = str(e)
if self.metrics:
self.metrics.record_test_completion(
test_name=f"ping_{sequence}",
test_type="ping",
start_time=start_time,
success=False,
server_name=self.server_config.name,
error_type=type(e).__name__,
metadata={"error": error_msg}
)
if self.logger:
self.logger.warning(f"Ping #{sequence} failed: {error_msg}")
return PingResult(
sequence=sequence,
success=False,
latency_ms=latency,
timestamp=timestamp,
error_message=error_msg,
metadata={"exception_type": type(e).__name__}
)
async def ping_multiple(self,
count: int = 10,
interval: float = 1.0,
timeout: float = None,
ping_method: str = None,
stop_on_failure: bool = False) -> PingStatistics:
"""Perform multiple ping tests with statistical analysis"""
if self.logger:
self.logger.info(f"Starting ping test: {count} pings with {interval}s interval")
ping_results = []
start_time = time.time()
for sequence in range(1, count + 1):
ping_result = await self.ping_once(
timeout=timeout,
sequence=sequence,
ping_method=ping_method
)
ping_results.append(ping_result)
# Stop on failure if requested
if stop_on_failure and not ping_result.success:
if self.logger:
self.logger.warning(f"Stopping ping test after failure at sequence {sequence}")
break
# Wait for interval (except for last ping)
if sequence < count:
await asyncio.sleep(interval)
total_duration = time.time() - start_time
statistics = self._calculate_ping_statistics(ping_results, total_duration)
if self.logger:
self.logger.info(f"Ping test completed: {statistics.successful_pings}/{statistics.total_pings} "
f"successful ({statistics.packet_loss_percent:.1f}% loss), "
f"avg latency: {statistics.avg_latency_ms:.1f}ms")
return statistics
async def ping_with_retry(self,
max_retries: int = None,
retry_delay: float = None,
backoff_multiplier: float = 1.5,
timeout: float = None) -> Tuple[bool, List[PingResult]]:
"""Ping with exponential backoff retry logic"""
if max_retries is None:
max_retries = self.default_retry_count
if retry_delay is None:
retry_delay = self.default_retry_delay
if timeout is None:
timeout = self.default_timeout
ping_results = []
current_delay = retry_delay
for attempt in range(max_retries + 1): # +1 for initial attempt
ping_result = await self.ping_once(timeout=timeout, sequence=attempt + 1)
ping_results.append(ping_result)
if ping_result.success:
if self.logger and attempt > 0:
self.logger.info(f"Ping succeeded on attempt {attempt + 1}")
return True, ping_results
# If this wasn't the last attempt, wait before retrying
if attempt < max_retries:
if self.logger:
self.logger.debug(f"Ping attempt {attempt + 1} failed, retrying in {current_delay:.1f}s")
await asyncio.sleep(current_delay)
current_delay *= backoff_multiplier
if self.logger:
self.logger.warning(f"Ping failed after {max_retries + 1} attempts")
return False, ping_results
async def reliability_test(self,
duration_minutes: float = 10.0,
ping_interval: float = 5.0,
timeout: float = None,
connection_test_interval: float = 60.0) -> ReliabilityTestResult:
"""Comprehensive reliability and stability testing"""
if self.logger:
self.logger.info(f"Starting reliability test for {duration_minutes} minutes")
test_start_time = time.time()
test_end_time = test_start_time + (duration_minutes * 60)
ping_results = []
connection_drops = 0
recovery_times = []
current_outage_start = None
was_connected = True
sequence = 1
while time.time() < test_end_time:
# Perform ping test
ping_result = await self.ping_once(timeout=timeout, sequence=sequence)
ping_results.append(ping_result)
sequence += 1
current_time = time.time()
# Track connection state changes
if ping_result.success and not was_connected:
# Connection recovered
if current_outage_start is not None:
recovery_time = current_time - current_outage_start
recovery_times.append(recovery_time)
current_outage_start = None
if self.logger:
self.logger.info(f"Connection recovered after {recovery_time:.1f}s outage")
was_connected = True
elif not ping_result.success and was_connected:
# Connection lost
connection_drops += 1
current_outage_start = current_time
was_connected = False
if self.logger:
self.logger.warning(f"Connection lost (drop #{connection_drops})")
# Wait for next ping
await asyncio.sleep(ping_interval)
# Handle ongoing outage at test end
if current_outage_start is not None:
final_outage_duration = time.time() - current_outage_start
recovery_times.append(final_outage_duration)
# Calculate statistics
total_duration = time.time() - test_start_time
ping_statistics = self._calculate_ping_statistics(ping_results, total_duration)
# Calculate additional reliability metrics
average_recovery_time = statistics.mean(recovery_times) if recovery_times else 0.0
longest_outage = max(recovery_times) if recovery_times else 0.0
# Calculate availability (time connected vs total time)
total_outage_time = sum(recovery_times)
availability_percent = ((total_duration - total_outage_time) / total_duration) * 100
# Calculate stability score (0-10)
stability_score = self._calculate_stability_score(
ping_statistics.success_rate,
connection_drops,
average_recovery_time,
ping_statistics.stddev_latency_ms
)
result = ReliabilityTestResult(
test_duration_minutes=total_duration / 60,
ping_statistics=ping_statistics,
connection_drops=connection_drops,
recovery_times=recovery_times,
average_recovery_time=average_recovery_time,
longest_outage_seconds=longest_outage,
availability_percent=availability_percent,
stability_score=stability_score
)
if self.logger:
self.logger.info(f"Reliability test completed: {availability_percent:.1f}% availability, "
f"{connection_drops} drops, stability score: {stability_score:.1f}/10")
return result
async def continuous_monitor(self,
ping_interval: float = 30.0,
alert_threshold_ms: float = 1000.0,
alert_callback: Optional[callable] = None) -> None:
"""Continuous monitoring with alerting"""
if self.logger:
self.logger.info(f"Starting continuous monitoring (interval: {ping_interval}s)")
sequence = 1
consecutive_failures = 0
try:
while True:
ping_result = await self.ping_once(sequence=sequence)
sequence += 1
# Check for alerts
if not ping_result.success:
consecutive_failures += 1
if alert_callback:
await alert_callback({
"type": "ping_failure",
"message": f"Ping failed: {ping_result.error_message}",
"consecutive_failures": consecutive_failures,
"timestamp": ping_result.timestamp
})
else:
if consecutive_failures > 0 and alert_callback:
await alert_callback({
"type": "ping_recovery",
"message": f"Ping recovered after {consecutive_failures} failures",
"latency_ms": ping_result.latency_ms,
"timestamp": ping_result.timestamp
})
consecutive_failures = 0
# Check for high latency
if ping_result.latency_ms > alert_threshold_ms and alert_callback:
await alert_callback({
"type": "high_latency",
"message": f"High latency detected: {ping_result.latency_ms:.1f}ms",
"latency_ms": ping_result.latency_ms,
"threshold_ms": alert_threshold_ms,
"timestamp": ping_result.timestamp
})
await asyncio.sleep(ping_interval)
except asyncio.CancelledError:
if self.logger:
self.logger.info("Continuous monitoring stopped")
except Exception as e:
if self.logger:
self.logger.error(f"Continuous monitoring error: {e}")
raise
def _calculate_ping_statistics(self, ping_results: List[PingResult], total_duration: float) -> PingStatistics:
"""Calculate comprehensive ping statistics"""
if not ping_results:
return PingStatistics(
total_pings=0, successful_pings=0, failed_pings=0,
packet_loss_percent=100.0, min_latency_ms=0.0, max_latency_ms=0.0,
avg_latency_ms=0.0, median_latency_ms=0.0, stddev_latency_ms=0.0,
jitter_ms=0.0, uptime_percent=0.0, total_duration_seconds=total_duration
)
total_pings = len(ping_results)
successful_pings = sum(1 for r in ping_results if r.success)
failed_pings = total_pings - successful_pings
packet_loss_percent = (failed_pings / total_pings) * 100
# Calculate latency statistics for successful pings only
successful_latencies = [r.latency_ms for r in ping_results if r.success]
if successful_latencies:
min_latency = min(successful_latencies)
max_latency = max(successful_latencies)
avg_latency = statistics.mean(successful_latencies)
median_latency = statistics.median(successful_latencies)
stddev_latency = statistics.stdev(successful_latencies) if len(successful_latencies) > 1 else 0.0
# Calculate jitter (average absolute difference from mean)
jitter = statistics.mean([abs(lat - avg_latency) for lat in successful_latencies])
else:
min_latency = max_latency = avg_latency = median_latency = stddev_latency = jitter = 0.0
# Calculate uptime percentage
uptime_percent = (successful_pings / total_pings) * 100
# Count error types
error_breakdown = {}
for result in ping_results:
if not result.success and result.error_message:
# Extract error type
if "timeout" in result.error_message.lower():
error_type = "timeout"
elif "connection" in result.error_message.lower():
error_type = "connection"
else:
error_type = "other"
error_breakdown[error_type] = error_breakdown.get(error_type, 0) + 1
return PingStatistics(
total_pings=total_pings,
successful_pings=successful_pings,
failed_pings=failed_pings,
packet_loss_percent=packet_loss_percent,
min_latency_ms=min_latency,
max_latency_ms=max_latency,
avg_latency_ms=avg_latency,
median_latency_ms=median_latency,
stddev_latency_ms=stddev_latency,
jitter_ms=jitter,
uptime_percent=uptime_percent,
total_duration_seconds=total_duration,
error_breakdown=error_breakdown
)
def _calculate_stability_score(self,
success_rate: float,
connection_drops: int,
avg_recovery_time: float,
latency_stddev: float) -> float:
"""Calculate stability score (0-10) based on various factors"""
# Base score from success rate (0-4 points)
success_score = (success_rate / 100) * 4
# Connection stability score (0-3 points)
# Penalize frequent drops
if connection_drops == 0:
stability_score = 3
elif connection_drops <= 2:
stability_score = 2
elif connection_drops <= 5:
stability_score = 1
else:
stability_score = 0
# Recovery time score (0-2 points)
# Reward fast recovery
if avg_recovery_time == 0:
recovery_score = 2
elif avg_recovery_time <= 10:
recovery_score = 1.5
elif avg_recovery_time <= 30:
recovery_score = 1
elif avg_recovery_time <= 60:
recovery_score = 0.5
else:
recovery_score = 0
# Latency consistency score (0-1 point)
# Reward consistent latency
if latency_stddev <= 10:
consistency_score = 1
elif latency_stddev <= 50:
consistency_score = 0.7
elif latency_stddev <= 100:
consistency_score = 0.4
else:
consistency_score = 0
total_score = success_score + stability_score + recovery_score + consistency_score
return min(10.0, max(0.0, total_score))
# Convenience functions for common ping operations
async def quick_ping(server_config: Any, timeout: float = 5.0) -> bool:
"""Quick connectivity check - returns True if server is reachable"""
tester = PingTester(server_config, enable_metrics=False, enable_logging=False)
result = await tester.ping_once(timeout=timeout)
return result.success
async def ping_with_stats(server_config: Any,
count: int = 10,
interval: float = 1.0,
timeout: float = 5.0) -> PingStatistics:
"""Ping test with statistical analysis"""
tester = PingTester(server_config, enable_metrics=False, enable_logging=True)
return await tester.ping_multiple(count=count, interval=interval, timeout=timeout)
async def test_server_reliability(server_config: Any,
duration_minutes: float = 5.0,
ping_interval: float = 5.0) -> ReliabilityTestResult:
"""Test server reliability over time"""
tester = PingTester(server_config, enable_metrics=True, enable_logging=True)
return await tester.reliability_test(duration_minutes=duration_minutes, ping_interval=ping_interval)
@asynccontextmanager
async def continuous_ping_monitor(server_config: Any,
ping_interval: float = 30.0,
alert_callback: Optional[callable] = None):
"""Context manager for continuous ping monitoring"""
tester = PingTester(server_config, enable_metrics=True, enable_logging=True)
monitor_task = asyncio.create_task(
tester.continuous_monitor(ping_interval=ping_interval, alert_callback=alert_callback)
)
try:
yield tester
finally:
monitor_task.cancel()
try:
await monitor_task
except asyncio.CancelledError:
pass

View File

@ -0,0 +1,13 @@
"""
MCPTesta Reporters
Output formatting and reporting for test results.
"""
from .html import HTMLReporter
from .console import ConsoleReporter
__all__ = [
"HTMLReporter",
"ConsoleReporter",
]

View File

@ -0,0 +1,820 @@
"""
Console Reporter for MCPTesta
Rich console reporting with real-time progress indicators, colored output,
test result summaries, and interactive features for beautiful terminal output.
"""
import asyncio
import time
from typing import Dict, Any, List, Optional
from dataclasses import dataclass
from datetime import datetime, timedelta
from rich.console import Console
from rich.progress import (
Progress,
TaskID,
SpinnerColumn,
TextColumn,
BarColumn,
MofNCompleteColumn,
TimeRemainingColumn,
TimeElapsedColumn
)
from rich.table import Table
from rich.panel import Panel
from rich.text import Text
from rich.live import Live
from rich.tree import Tree
from rich.status import Status
from rich.columns import Columns
from rich.align import Align
from ..core.client import TestResult
from ..runners.parallel import ExecutionStats
from ..utils.logging import get_logger
@dataclass
class TestSummary:
"""Summary statistics for test execution"""
total_tests: int = 0
passed: int = 0
failed: int = 0
skipped: int = 0
cancelled: int = 0
total_time: float = 0.0
fastest_test: Optional[str] = None
slowest_test: Optional[str] = None
fastest_time: float = float('inf')
slowest_time: float = 0.0
@property
def success_rate(self) -> float:
"""Calculate success rate percentage"""
if self.total_tests == 0:
return 0.0
return (self.passed / self.total_tests) * 100
@property
def completion_rate(self) -> float:
"""Calculate completion rate percentage"""
if self.total_tests == 0:
return 0.0
completed = self.passed + self.failed
return (completed / self.total_tests) * 100
class ConsoleReporter:
"""
Rich console reporter with beautiful terminal output.
Features:
- Real-time progress indicators with spinner and progress bars
- Colored output with success/failure indicators
- Detailed test result summaries with statistics
- Interactive features (if supported by terminal)
- Beautiful formatting with Rich library components
- Live updating displays during test execution
"""
def __init__(self,
console: Optional[Console] = None,
show_progress: bool = True,
show_details: bool = True,
interactive_mode: bool = True,
show_performance_metrics: bool = True,
show_resource_usage: bool = True):
self.console = console or Console()
self.show_progress = show_progress
self.show_details = show_details
self.interactive_mode = interactive_mode
self.show_performance_metrics = show_performance_metrics
self.show_resource_usage = show_resource_usage
self.logger = get_logger(__name__)
# Progress tracking
self.progress: Optional[Progress] = None
self.main_task: Optional[TaskID] = None
self.layer_task: Optional[TaskID] = None
self.live_display: Optional[Live] = None
# Test tracking
self.summary = TestSummary()
self.test_results: List[TestResult] = []
self.current_layer = 0
self.total_layers = 0
self.start_time = time.time()
# Enhanced metrics integration
self.metrics_collector = None
self.session_data = None
# Real-time status
self.current_status = "Initializing..."
self.last_update = time.time()
async def start_session(self, total_tests: int, total_layers: int = 1,
metrics_collector=None, session_data=None):
"""Start reporting session with enhanced metrics integration"""
self.summary.total_tests = total_tests
self.total_layers = total_layers
self.start_time = time.time()
# Enhanced metrics integration
self.metrics_collector = metrics_collector
self.session_data = session_data
if self.show_progress:
self._setup_progress_display()
self._show_session_banner()
def _setup_progress_display(self):
"""Setup progress display components"""
# Create progress bar with multiple columns
self.progress = Progress(
SpinnerColumn(style="cyan"),
TextColumn("[bold blue]{task.fields[test_name]}", justify="left"),
BarColumn(complete_style="green", finished_style="bright_green"),
MofNCompleteColumn(),
TimeElapsedColumn(),
TimeRemainingColumn(),
console=self.console,
refresh_per_second=10
)
# Add main progress task
self.main_task = self.progress.add_task(
"overall",
test_name="Overall Progress",
total=self.summary.total_tests
)
# Add layer progress task if multiple layers
if self.total_layers > 1:
self.layer_task = self.progress.add_task(
"layer",
test_name="Current Layer",
total=0
)
def _show_session_banner(self):
"""Display session start banner"""
banner_text = Text()
banner_text.append("🧪 MCPTesta Test Session Started\n", style="bold cyan")
banner_text.append(f"📊 Total Tests: {self.summary.total_tests}\n", style="white")
banner_text.append(f"🏗️ Execution Layers: {self.total_layers}\n", style="white")
banner_text.append(f"⏰ Started: {datetime.now().strftime('%H:%M:%S')}", style="dim")
panel = Panel(
banner_text,
title="Test Execution",
border_style="cyan",
padding=(1, 2)
)
self.console.print(panel)
self.console.print() # Add spacing
async def report_layer_start(self, layer_index: int, layer_tests: int):
"""Report start of test layer execution"""
self.current_layer = layer_index
layer_name = f"Layer {layer_index + 1}/{self.total_layers}"
if self.progress and self.layer_task:
self.progress.update(
self.layer_task,
test_name=f"{layer_name} ({layer_tests} tests)",
completed=0,
total=layer_tests
)
if not self.show_progress:
self.console.print(
f"🔄 Starting {layer_name}: {layer_tests} tests",
style="bold yellow"
)
async def report_test_start(self, test_name: str):
"""Report start of individual test"""
self.current_status = f"Running: {test_name}"
if self.show_details and not self.show_progress:
self.console.print(f" ▶️ {test_name}", style="dim")
async def report_test_result(self, result: TestResult):
"""Report individual test result"""
self.test_results.append(result)
# Update summary statistics
if result.success:
self.summary.passed += 1
status_icon = ""
status_style = "green"
else:
self.summary.failed += 1
status_icon = ""
status_style = "red"
# Track timing
if result.execution_time < self.summary.fastest_time:
self.summary.fastest_time = result.execution_time
self.summary.fastest_test = result.test_name
if result.execution_time > self.summary.slowest_time:
self.summary.slowest_time = result.execution_time
self.summary.slowest_test = result.test_name
# Update progress
if self.progress:
self.progress.update(self.main_task, advance=1)
if self.layer_task:
self.progress.update(self.layer_task, advance=1)
# Show detailed result if not using progress bars
if self.show_details and not self.show_progress:
execution_time = f"{result.execution_time:.3f}s"
self.console.print(
f" {status_icon} {result.test_name} ({execution_time})",
style=status_style
)
if not result.success and result.error_message:
self.console.print(
f" 💥 {result.error_message}",
style="red dim"
)
async def report_layer_completion(self, layer_index: int, layer_results: List[TestResult]):
"""Report completion of test layer"""
layer_passed = sum(1 for r in layer_results if r.success)
layer_failed = len(layer_results) - layer_passed
total_time = sum(r.execution_time for r in layer_results)
if not self.show_progress:
self.console.print(
f"✅ Layer {layer_index + 1} complete: "
f"{layer_passed} passed, {layer_failed} failed "
f"({total_time:.2f}s)",
style="green" if layer_failed == 0 else "yellow"
)
self.console.print() # Add spacing
async def report_session_complete(self, execution_stats: ExecutionStats):
"""Report completion of test session with comprehensive summary"""
self.summary.total_time = execution_stats.execution_time
# Stop progress display
if self.progress:
self.progress.stop()
# Show completion banner
self._show_completion_banner(execution_stats)
# Show detailed summary
if self.show_details:
await self._show_detailed_summary(execution_stats)
# Show enhanced performance metrics
if self.show_performance_metrics and self.metrics_collector:
await self._show_performance_metrics()
# Show resource usage analysis
if self.show_resource_usage and self.metrics_collector:
await self._show_resource_usage_summary()
# Show server capabilities if available
if self.session_data and self.session_data.server_capabilities:
await self._show_server_capabilities()
# Show failed tests if any
if self.summary.failed > 0:
await self._show_failed_tests()
def _show_completion_banner(self, execution_stats: ExecutionStats):
"""Display session completion banner"""
# Determine overall status
if self.summary.failed == 0:
banner_style = "green"
status_icon = "🎉"
status_text = "ALL TESTS PASSED"
elif self.summary.passed > 0:
banner_style = "yellow"
status_icon = "⚠️"
status_text = "SOME TESTS FAILED"
else:
banner_style = "red"
status_icon = "💥"
status_text = "ALL TESTS FAILED"
# Create summary text
summary_text = Text()
summary_text.append(f"{status_icon} {status_text}\n", style=f"bold {banner_style}")
summary_text.append(
f"📊 Results: {self.summary.passed} passed, "
f"{self.summary.failed} failed, "
f"{self.summary.skipped} skipped\n",
style="white"
)
summary_text.append(
f"⏱️ Total Time: {self.summary.total_time:.2f}s\n",
style="white"
)
summary_text.append(
f"📈 Success Rate: {self.summary.success_rate:.1f}%",
style="white"
)
panel = Panel(
summary_text,
title="Test Results",
border_style=banner_style,
padding=(1, 2)
)
self.console.print(panel)
async def _show_detailed_summary(self, execution_stats: ExecutionStats):
"""Show detailed test execution summary"""
self.console.print("\n📋 Detailed Summary", style="bold cyan")
# Create summary table
summary_table = Table(show_header=True, header_style="bold magenta")
summary_table.add_column("Metric", style="cyan", min_width=20)
summary_table.add_column("Value", justify="right")
summary_table.add_column("Additional Info", style="dim")
# Add summary rows
summary_table.add_row(
"Total Tests",
str(self.summary.total_tests),
""
)
summary_table.add_row(
"Passed",
str(self.summary.passed),
f"{self.summary.success_rate:.1f}% success rate"
)
summary_table.add_row(
"Failed",
str(self.summary.failed),
f"{100 - self.summary.success_rate:.1f}% failure rate"
)
summary_table.add_row(
"Skipped",
str(self.summary.skipped),
""
)
summary_table.add_row(
"Total Time",
f"{self.summary.total_time:.3f}s",
""
)
if self.summary.fastest_test:
summary_table.add_row(
"Fastest Test",
f"{self.summary.fastest_time:.3f}s",
self.summary.fastest_test
)
if self.summary.slowest_test:
summary_table.add_row(
"Slowest Test",
f"{self.summary.slowest_time:.3f}s",
self.summary.slowest_test
)
# Add parallel execution stats if available
if hasattr(execution_stats, 'parallel_efficiency'):
summary_table.add_row(
"Parallel Efficiency",
f"{execution_stats.parallel_efficiency:.1f}%",
"Worker utilization"
)
self.console.print(summary_table)
# Show performance breakdown if available
if hasattr(execution_stats, 'worker_utilization') and execution_stats.worker_utilization:
await self._show_worker_utilization(execution_stats.worker_utilization)
async def _show_worker_utilization(self, worker_stats: Dict[int, float]):
"""Show worker utilization statistics"""
self.console.print("\n⚡ Worker Utilization", style="bold cyan")
worker_table = Table(show_header=True, header_style="bold magenta")
worker_table.add_column("Worker ID", justify="center")
worker_table.add_column("Utilization", justify="right")
worker_table.add_column("Status", justify="center")
for worker_id, utilization in worker_stats.items():
if utilization >= 80:
status = "🔥 High"
status_style = "green"
elif utilization >= 50:
status = "⚡ Good"
status_style = "yellow"
else:
status = "💤 Low"
status_style = "red"
worker_table.add_row(
str(worker_id),
f"{utilization:.1f}%",
Text(status, style=status_style)
)
self.console.print(worker_table)
async def _show_failed_tests(self):
"""Show details of failed tests"""
failed_tests = [r for r in self.test_results if not r.success]
if not failed_tests:
return
self.console.print(f"\n❌ Failed Tests ({len(failed_tests)})", style="bold red")
for i, result in enumerate(failed_tests, 1):
# Create failure panel
failure_text = Text()
failure_text.append(f"Test: {result.test_name}\n", style="bold red")
failure_text.append(f"Duration: {result.execution_time:.3f}s\n", style="dim")
if result.error_message:
failure_text.append(f"Error: {result.error_message}\n", style="red")
if result.metadata:
failure_text.append("Metadata:\n", style="dim")
for key, value in result.metadata.items():
failure_text.append(f" {key}: {value}\n", style="dim")
panel = Panel(
failure_text,
title=f"Failure #{i}",
border_style="red",
padding=(1, 2)
)
self.console.print(panel)
def show_live_status(self):
"""Show live updating status during test execution"""
if not self.interactive_mode or not self.progress:
return
# Create live display layout
layout = self._create_live_layout()
self.live_display = Live(
layout,
console=self.console,
refresh_per_second=4,
transient=True
)
self.live_display.start()
def _create_live_layout(self):
"""Create live layout with progress and status"""
# Current status
status_text = Text(self.current_status, style="bold yellow")
# Progress bars
progress_panel = Panel(
self.progress,
title="Progress",
border_style="cyan"
)
# Quick stats
stats_text = Text()
stats_text.append(f"✅ Passed: {self.summary.passed} ", style="green")
stats_text.append(f"❌ Failed: {self.summary.failed} ", style="red")
stats_text.append(f"⏱️ Elapsed: {time.time() - self.start_time:.1f}s", style="cyan")
# Combine components
return Columns([
Panel(
Align.center(status_text),
title="Status",
border_style="yellow"
),
Panel(
Align.center(stats_text),
title="Stats",
border_style="blue"
)
])
def stop_live_display(self):
"""Stop live display"""
if self.live_display:
self.live_display.stop()
self.live_display = None
async def show_test_tree(self, test_suites: List[Any]):
"""Show test tree structure"""
self.console.print("📋 Test Structure", style="bold cyan")
tree = Tree("🧪 MCPTesta Test Suites")
for suite in test_suites:
suite_node = tree.add(f"📦 {suite.name}")
for test in suite.tests:
if not test.enabled:
suite_node.add(f"⏸️ {test.name} (disabled)", style="dim")
else:
test_icon = "🔧" if test.test_type == "tool_call" else "📄"
suite_node.add(f"{test_icon} {test.name}")
self.console.print(tree)
self.console.print()
def print_error(self, message: str, exception: Optional[Exception] = None):
"""Print error message with formatting"""
error_text = f"{message}"
if exception:
error_text += f": {exception}"
self.console.print(error_text, style="bold red")
def print_warning(self, message: str):
"""Print warning message with formatting"""
self.console.print(f"⚠️ {message}", style="bold yellow")
def print_info(self, message: str):
"""Print info message with formatting"""
self.console.print(f" {message}", style="bold blue")
def print_success(self, message: str):
"""Print success message with formatting"""
self.console.print(f"{message}", style="bold green")
async def _show_performance_metrics(self):
"""Show enhanced performance metrics from metrics collector"""
if not self.metrics_collector:
return
self.console.print("\n⚡ Performance Analysis", style="bold cyan")
# Get performance stats
perf_stats = self.metrics_collector.performance_stats
if not perf_stats:
self.console.print("No performance data available", style="dim")
return
# Create performance table
perf_table = Table(show_header=True, header_style="bold magenta")
perf_table.add_column("Operation Type", style="cyan", min_width=15)
perf_table.add_column("Calls", justify="right")
perf_table.add_column("Success Rate", justify="right")
perf_table.add_column("Avg Time", justify="right")
perf_table.add_column("P95 Time", justify="right")
perf_table.add_column("P99 Time", justify="right")
perf_table.add_column("Errors", justify="right")
for op_type, stats in perf_stats.items():
# Format operation type for display
display_name = op_type.replace('_', ' ').title()
# Determine success rate color
success_color = "green" if stats.success_rate >= 95 else "yellow" if stats.success_rate >= 80 else "red"
perf_table.add_row(
display_name,
str(stats.total_calls),
Text(f"{stats.success_rate:.1f}%", style=success_color),
f"{stats.average_time:.3f}s",
f"{stats.percentile_95:.3f}s",
f"{stats.percentile_99:.3f}s",
str(len(stats.error_types))
)
self.console.print(perf_table)
# Show latency distribution for most used operation
if perf_stats:
most_used = max(perf_stats.items(), key=lambda x: x[1].total_calls)
await self._show_latency_distribution(most_used[0], most_used[1])
async def _show_latency_distribution(self, operation_name: str, stats):
"""Show latency distribution for an operation"""
if not stats.latency_buckets:
return
self.console.print(f"\n📊 Latency Distribution - {operation_name.replace('_', ' ').title()}", style="bold cyan")
total_calls = sum(stats.latency_buckets.values())
if total_calls == 0:
return
# Create latency distribution display
for bucket, count in stats.latency_buckets.items():
if count == 0:
continue
percentage = (count / total_calls) * 100
bar_length = int(percentage / 2) # Scale to fit console
bar = "" * bar_length + "" * (50 - bar_length)
color = "green" if bucket in ["0-100ms", "100-500ms"] else "yellow" if bucket == "500ms-1s" else "red"
self.console.print(
f" {bucket:>10}{bar}{count:>4} ({percentage:>5.1f}%)",
style=color
)
async def _show_resource_usage_summary(self):
"""Show resource usage summary and trends"""
if not self.metrics_collector or not self.metrics_collector.resource_snapshots:
return
self.console.print("\n💾 Resource Usage Summary", style="bold cyan")
# Get resource metrics
resource_metrics = self.metrics_collector.resource_metrics
snapshots = list(self.metrics_collector.resource_snapshots)
# Create resource summary table
resource_table = Table(show_header=True, header_style="bold magenta")
resource_table.add_column("Resource", style="cyan")
resource_table.add_column("Current", justify="right")
resource_table.add_column("Peak", justify="right")
resource_table.add_column("Trend", justify="center")
# Memory usage
current_memory = resource_metrics.get('current_memory_mb', 0)
peak_memory = resource_metrics.get('peak_memory_mb', 0)
memory_trend = self._calculate_trend([s.memory_mb for s in snapshots[-10:]])
memory_color = "red" if peak_memory > 500 else "yellow" if peak_memory > 200 else "green"
resource_table.add_row(
"Memory",
Text(f"{current_memory:.1f} MB", style=memory_color),
Text(f"{peak_memory:.1f} MB", style=memory_color),
Text(memory_trend, style="cyan")
)
# CPU usage
current_cpu = resource_metrics.get('cpu_usage_percent', 0)
peak_cpu = resource_metrics.get('peak_cpu_percent', 0)
cpu_trend = self._calculate_trend([s.cpu_percent for s in snapshots[-10:]])
cpu_color = "red" if peak_cpu > 80 else "yellow" if peak_cpu > 50 else "green"
resource_table.add_row(
"CPU",
Text(f"{current_cpu:.1f}%", style=cpu_color),
Text(f"{peak_cpu:.1f}%", style=cpu_color),
Text(cpu_trend, style="cyan")
)
# Active connections
current_connections = resource_metrics.get('active_connections', 0)
peak_connections = resource_metrics.get('peak_connections', 0)
connection_trend = self._calculate_trend([s.active_connections for s in snapshots[-10:]])
resource_table.add_row(
"Connections",
str(current_connections),
str(peak_connections),
Text(connection_trend, style="cyan")
)
# Active threads
current_threads = resource_metrics.get('active_threads', 0)
peak_threads = resource_metrics.get('peak_threads', 0)
thread_trend = self._calculate_trend([s.active_threads for s in snapshots[-10:]])
resource_table.add_row(
"Threads",
str(current_threads),
str(peak_threads),
Text(thread_trend, style="cyan")
)
self.console.print(resource_table)
# Show efficiency metrics
if len(snapshots) > 1:
await self._show_efficiency_analysis()
def _calculate_trend(self, values: List[float]) -> str:
"""Calculate trend from a list of values"""
if len(values) < 2:
return ""
# Simple linear trend calculation
start_avg = sum(values[:len(values)//2]) / (len(values)//2)
end_avg = sum(values[len(values)//2:]) / (len(values) - len(values)//2)
if end_avg > start_avg * 1.1:
return "📈" # Increasing
elif end_avg < start_avg * 0.9:
return "📉" # Decreasing
else:
return "➡️" # Stable
async def _show_efficiency_analysis(self):
"""Show parallel execution efficiency analysis"""
self.console.print("\n🎯 Efficiency Analysis", style="bold cyan")
# Get parallel efficiency from metrics
summary_stats = self.metrics_collector.get_summary_stats()
efficiency_panel = Panel(
f"[bold green]Parallel Efficiency: {summary_stats.get('parallel_efficiency', 0):.1f}%[/bold green]\n"
f"[cyan]Connection Pool Hit Rate: {self._calculate_connection_hit_rate():.1f}%[/cyan]\n"
f"[yellow]Average Test Throughput: {self._calculate_test_throughput():.2f} tests/sec[/yellow]",
title="Performance Insights",
border_style="green",
padding=(1, 2)
)
self.console.print(efficiency_panel)
def _calculate_connection_hit_rate(self) -> float:
"""Calculate connection pool hit rate"""
if not self.metrics_collector:
return 0.0
connection_metrics = self.metrics_collector.connection_metrics
hits = connection_metrics.get('connection_pool_hits', 0)
misses = connection_metrics.get('connection_pool_misses', 0)
total = hits + misses
return (hits / total * 100) if total > 0 else 0.0
def _calculate_test_throughput(self) -> float:
"""Calculate tests per second"""
total_time = time.time() - self.start_time
return self.summary.total_tests / total_time if total_time > 0 else 0.0
async def _show_server_capabilities(self):
"""Show discovered server capabilities"""
if not self.session_data or not self.session_data.server_capabilities:
return
self.console.print("\n🔍 Server Capabilities", style="bold cyan")
for server_name, capabilities in self.session_data.server_capabilities.items():
# Create capability summary
cap_text = Text()
cap_text.append(f"📡 {server_name}\n", style="bold white")
cap_text.append(f" Tools: {len(capabilities.tools)}\n", style="green")
cap_text.append(f" Resources: {len(capabilities.resources)}\n", style="blue")
cap_text.append(f" Prompts: {len(capabilities.prompts)}\n", style="yellow")
# Add advanced features
features = []
if capabilities.supports_notifications:
features.append("🔔 Notifications")
if capabilities.supports_cancellation:
features.append("🛑 Cancellation")
if capabilities.supports_progress:
features.append("📊 Progress")
if capabilities.supports_sampling:
features.append("🎯 Sampling")
if features:
cap_text.append(f" Features: {', '.join(features)}", style="cyan")
capability_panel = Panel(
cap_text,
border_style="blue",
padding=(0, 1)
)
self.console.print(capability_panel)

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,13 @@
"""
MCPTesta Test Runners
Test execution engines for parallel and sequential test running.
"""
from .parallel import ParallelTestRunner
from .sequential import SequentialTestRunner
__all__ = [
"ParallelTestRunner",
"SequentialTestRunner",
]

View File

@ -0,0 +1,414 @@
"""
Parallel Test Runner
Advanced parallel execution system for FastMCP testing with intelligent workload
distribution, dependency resolution, and comprehensive result aggregation.
"""
import asyncio
import time
from concurrent.futures import ThreadPoolExecutor
from typing import Dict, Any, List, Optional, Set
from dataclasses import dataclass, field
from collections import defaultdict
from ..core.client import MCPTestClient, TestResult
from ..core.config import TestConfig
from ..core.session import TestSession
from ..yaml_parser.parser import TestCase, TestSuite
from ..utils.logging import get_logger
from ..utils.metrics import MetricsCollector
@dataclass
class ExecutionStats:
"""Statistics for test execution"""
total_tests: int = 0
passed: int = 0
failed: int = 0
skipped: int = 0
cancelled: int = 0
execution_time: float = 0.0
parallel_efficiency: float = 0.0
worker_utilization: Dict[int, float] = field(default_factory=dict)
dependency_resolution_time: float = 0.0
@dataclass
class WorkerStats:
"""Statistics for individual worker"""
worker_id: int
tests_executed: int = 0
total_time: float = 0.0
idle_time: float = 0.0
errors: int = 0
@property
def utilization(self) -> float:
"""Calculate worker utilization percentage"""
if self.total_time == 0:
return 0.0
return (self.total_time - self.idle_time) / self.total_time * 100
class DependencyResolver:
"""Resolves test dependencies and creates execution plan"""
def __init__(self, test_suites: List[TestSuite]):
self.test_suites = test_suites
self.logger = get_logger(__name__)
def create_execution_plan(self) -> List[List[TestCase]]:
"""Create execution plan with dependency resolution"""
start_time = time.time()
# Build dependency graph
all_tests = {}
dependencies = defaultdict(set)
dependents = defaultdict(set)
for suite in self.test_suites:
for test in suite.tests:
if not test.enabled:
continue
all_tests[test.name] = test
for dep in test.depends_on:
dependencies[test.name].add(dep)
dependents[dep].add(test.name)
# Topological sort for execution layers
execution_layers = []
remaining_tests = set(all_tests.keys())
while remaining_tests:
# Find tests with no unmet dependencies
ready_tests = []
for test_name in remaining_tests:
if not dependencies[test_name] or dependencies[test_name].isdisjoint(remaining_tests):
ready_tests.append(all_tests[test_name])
if not ready_tests:
# Circular dependency detected
self.logger.error(f"Circular dependency detected in tests: {remaining_tests}")
# Add remaining tests to final layer to avoid infinite loop
ready_tests = [all_tests[name] for name in remaining_tests]
execution_layers.append(ready_tests)
remaining_tests -= {test.name for test in ready_tests}
resolution_time = time.time() - start_time
self.logger.info(f"Dependency resolution completed in {resolution_time:.3f}s, {len(execution_layers)} layers")
return execution_layers
class ParallelTestRunner:
"""
Advanced parallel test runner with intelligent workload distribution.
Features:
- Dependency-aware execution planning
- Dynamic worker pool management
- Load balancing across servers
- Comprehensive metrics and monitoring
- Graceful error handling and recovery
"""
def __init__(self,
config: TestConfig,
reporters: List[Any] = None):
self.config = config
self.reporters = reporters or []
self.logger = get_logger(__name__)
self.metrics = MetricsCollector()
# Execution state
self._workers: Dict[int, WorkerStats] = {}
self._execution_stats = ExecutionStats()
self._cancellation_event = asyncio.Event()
self._results: List[TestResult] = []
async def run(self, session: TestSession) -> ExecutionStats:
"""Run all tests with parallel execution"""
start_time = time.time()
self.logger.info(f"Starting parallel test execution with {self.config.parallel_workers} workers")
try:
# Resolve dependencies and create execution plan
resolver = DependencyResolver(self.config.test_suites)
execution_layers = resolver.create_execution_plan()
self._execution_stats.dependency_resolution_time = time.time() - start_time
# Initialize workers
await self._initialize_workers()
# Execute tests layer by layer
for layer_index, test_layer in enumerate(execution_layers):
if self._cancellation_event.is_set():
break
self.logger.info(f"Executing layer {layer_index + 1}/{len(execution_layers)} ({len(test_layer)} tests)")
layer_results = await self._execute_test_layer(test_layer, session)
self._results.extend(layer_results)
# Update statistics
for result in layer_results:
if result.success:
self._execution_stats.passed += 1
else:
self._execution_stats.failed += 1
# Report layer completion
for reporter in self.reporters:
await reporter.report_layer_completion(layer_index, layer_results)
# Calculate final statistics
self._execution_stats.total_tests = len(self._results)
self._execution_stats.execution_time = time.time() - start_time
self._execution_stats.parallel_efficiency = self._calculate_efficiency()
self._execution_stats.worker_utilization = {
worker_id: stats.utilization
for worker_id, stats in self._workers.items()
}
self.logger.info(f"Test execution completed: {self._execution_stats.passed} passed, "
f"{self._execution_stats.failed} failed in {self._execution_stats.execution_time:.2f}s")
return self._execution_stats
except Exception as e:
self.logger.error(f"Test execution failed: {e}")
raise
finally:
await self._cleanup_workers()
async def _initialize_workers(self):
"""Initialize worker pool"""
for worker_id in range(self.config.parallel_workers):
self._workers[worker_id] = WorkerStats(worker_id=worker_id)
self.logger.debug(f"Initialized {len(self._workers)} workers")
async def _execute_test_layer(self,
tests: List[TestCase],
session: TestSession) -> List[TestResult]:
"""Execute a layer of tests in parallel"""
if not tests:
return []
# Distribute tests across available servers
server_test_groups = self._distribute_tests_across_servers(tests)
# Create worker tasks
tasks = []
for server_config, server_tests in server_test_groups.items():
for test_batch in self._batch_tests(server_tests):
task = asyncio.create_task(
self._execute_test_batch(test_batch, server_config, session)
)
tasks.append(task)
# Execute all batches concurrently
batch_results = await asyncio.gather(*tasks, return_exceptions=True)
# Flatten results
layer_results = []
for result in batch_results:
if isinstance(result, Exception):
self.logger.error(f"Batch execution failed: {result}")
continue
layer_results.extend(result)
return layer_results
def _distribute_tests_across_servers(self,
tests: List[TestCase]) -> Dict[Any, List[TestCase]]:
"""Distribute tests across available servers for load balancing"""
server_groups = defaultdict(list)
# Simple round-robin distribution
for i, test in enumerate(tests):
server_index = i % len(self.config.servers)
server_config = self.config.servers[server_index]
server_groups[server_config].append(test)
return server_groups
def _batch_tests(self, tests: List[TestCase], batch_size: int = 5) -> List[List[TestCase]]:
"""Batch tests for optimal worker utilization"""
batches = []
for i in range(0, len(tests), batch_size):
batch = tests[i:i + batch_size]
batches.append(batch)
return batches
async def _execute_test_batch(self,
tests: List[TestCase],
server_config: Any,
session: TestSession) -> List[TestResult]:
"""Execute a batch of tests on a specific server"""
worker_id = asyncio.current_task().get_name()
batch_start_time = time.time()
results = []
try:
# Create test client for this server
test_client = MCPTestClient(server_config)
async with test_client.connect():
for test in tests:
if self._cancellation_event.is_set():
break
result = await self._execute_single_test(test, test_client)
results.append(result)
# Update worker stats
if hash(worker_id) % len(self._workers) in self._workers:
worker_stats = self._workers[hash(worker_id) % len(self._workers)]
worker_stats.tests_executed += 1
worker_stats.total_time += result.execution_time
if not result.success:
worker_stats.errors += 1
except Exception as e:
self.logger.error(f"Batch execution failed: {e}")
# Create failure results for remaining tests
for test in tests[len(results):]:
results.append(TestResult(
test_name=test.name,
success=False,
execution_time=0.0,
error_message=f"Batch execution failed: {e}"
))
batch_time = time.time() - batch_start_time
self.logger.debug(f"Batch completed: {len(tests)} tests in {batch_time:.3f}s")
return results
async def _execute_single_test(self,
test: TestCase,
client: MCPTestClient) -> TestResult:
"""Execute a single test case"""
try:
if test.test_type == "tool_call":
result = await client.call_tool(
tool_name=test.target,
parameters=test.parameters,
timeout=test.timeout,
enable_cancellation=test.enable_cancellation,
enable_progress=test.enable_progress,
enable_sampling=test.enable_sampling,
sampling_rate=test.sampling_rate
)
elif test.test_type == "resource_read":
result = await client.read_resource(
resource_uri=test.target,
timeout=test.timeout
)
elif test.test_type == "prompt_get":
result = await client.get_prompt(
prompt_name=test.target,
arguments=test.parameters,
timeout=test.timeout
)
elif test.test_type == "ping":
result = await client.ping(timeout=test.timeout)
else:
result = TestResult(
test_name=test.name,
success=False,
execution_time=0.0,
error_message=f"Unknown test type: {test.test_type}"
)
# Validate expected results if configured
if test.expected_result and result.success:
validation_result = self._validate_test_result(result, test.expected_result)
if not validation_result:
result.success = False
result.error_message = "Result validation failed"
# Override test name for better reporting
result.test_name = test.name
return result
except Exception as e:
return TestResult(
test_name=test.name,
success=False,
execution_time=0.0,
error_message=str(e)
)
def _validate_test_result(self, result: TestResult, expected: Dict[str, Any]) -> bool:
"""Validate test result against expected values"""
if not result.response_data:
return False
# Simple validation - can be extended
for key, expected_value in expected.items():
if key not in result.response_data:
return False
if result.response_data[key] != expected_value:
return False
return True
def _calculate_efficiency(self) -> float:
"""Calculate parallel execution efficiency"""
if not self._workers or self._execution_stats.execution_time == 0:
return 0.0
# Efficiency = (Total work time) / (Wall clock time * Worker count)
total_work_time = sum(stats.total_time - stats.idle_time for stats in self._workers.values())
theoretical_max_time = self._execution_stats.execution_time * len(self._workers)
if theoretical_max_time == 0:
return 0.0
return (total_work_time / theoretical_max_time) * 100
async def cancel_execution(self):
"""Cancel ongoing test execution"""
self.logger.info("Cancelling test execution")
self._cancellation_event.set()
async def _cleanup_workers(self):
"""Cleanup worker resources"""
# Cleanup implementation
pass
@property
def results(self) -> List[TestResult]:
"""Get all test results"""
return self._results
@property
def execution_stats(self) -> ExecutionStats:
"""Get execution statistics"""
return self._execution_stats

View File

@ -0,0 +1,527 @@
"""
Sequential Test Runner for MCPTesta
Simple non-parallel execution with detailed logging, progress tracking,
and comprehensive error handling. Alternative to parallel runner for
debugging and scenarios requiring sequential execution.
"""
import asyncio
import time
from typing import Dict, Any, List, Optional
from dataclasses import dataclass, field
from datetime import datetime
from ..core.client import MCPTestClient, TestResult
from ..core.config import TestConfig
from ..core.session import TestSession
from ..yaml_parser.parser import TestCase, TestSuite
from ..utils.logging import get_logger
from ..utils.metrics import MetricsCollector
@dataclass
class SequentialExecutionStats:
"""Statistics for sequential test execution"""
total_tests: int = 0
passed: int = 0
failed: int = 0
skipped: int = 0
cancelled: int = 0
execution_time: float = 0.0
test_times: Dict[str, float] = field(default_factory=dict)
suite_times: Dict[str, float] = field(default_factory=dict)
errors: List[Dict[str, Any]] = field(default_factory=list)
@property
def success_rate(self) -> float:
"""Calculate success rate percentage"""
if self.total_tests == 0:
return 0.0
return (self.passed / self.total_tests) * 100
def has_failures(self) -> bool:
"""Check if there were any test failures"""
return self.failed > 0
def add_error(self, test_name: str, error: str, metadata: Optional[Dict[str, Any]] = None):
"""Add error to error log"""
self.errors.append({
"test_name": test_name,
"error": error,
"timestamp": datetime.now().isoformat(),
"metadata": metadata or {}
})
class SequentialTestRunner:
"""
Sequential test runner with comprehensive logging and error handling.
Features:
- Simple non-parallel execution for debugging and testing
- Detailed logging and progress tracking at each step
- Comprehensive error handling with detailed error reporting
- Integration with same interfaces as parallel runner
- Support for all MCP protocol features (notifications, cancellation, etc.)
- Graceful cleanup and resource management
- Metrics collection and performance tracking
"""
def __init__(self,
config: TestConfig,
reporters: List[Any] = None):
self.config = config
self.reporters = reporters or []
self.logger = get_logger(__name__)
self.metrics = MetricsCollector() if hasattr(config, 'enable_metrics') else None
# Execution state
self._execution_stats = SequentialExecutionStats()
self._cancellation_requested = False
self._current_test: Optional[str] = None
self._results: List[TestResult] = []
self._cleanup_tasks: List[Any] = []
async def run(self, session: TestSession) -> SequentialExecutionStats:
"""Run all tests sequentially"""
start_time = time.time()
self.logger.info("Starting sequential test execution")
try:
# Count total tests
total_tests = sum(
len([test for test in suite.tests if test.enabled])
for suite in self.config.test_suites
)
self._execution_stats.total_tests = total_tests
self.logger.info(f"Found {total_tests} tests across {len(self.config.test_suites)} suites")
# Notify reporters of session start
for reporter in self.reporters:
if hasattr(reporter, 'start_session'):
await reporter.start_session(total_tests, len(self.config.test_suites))
# Execute test suites sequentially
for suite_index, suite in enumerate(self.config.test_suites):
if self._cancellation_requested:
self.logger.info("Test execution cancelled by user")
break
suite_start_time = time.time()
await self._execute_test_suite(suite, suite_index)
suite_execution_time = time.time() - suite_start_time
self._execution_stats.suite_times[suite.name] = suite_execution_time
self.logger.info(f"Suite '{suite.name}' completed in {suite_execution_time:.2f}s")
# Calculate final statistics
self._execution_stats.execution_time = time.time() - start_time
self.logger.info(f"Sequential execution completed: {self._execution_stats.passed} passed, "
f"{self._execution_stats.failed} failed in {self._execution_stats.execution_time:.2f}s")
# Notify reporters of completion
for reporter in self.reporters:
if hasattr(reporter, 'report_session_complete'):
await reporter.report_session_complete(self._execution_stats)
return self._execution_stats
except Exception as e:
self.logger.error(f"Sequential test execution failed: {e}")
self._execution_stats.add_error("__execution__", str(e))
raise
finally:
await self._cleanup_resources()
async def _execute_test_suite(self, suite: TestSuite, suite_index: int):
"""Execute a single test suite"""
self.logger.info(f"Starting test suite: {suite.name}")
# Filter enabled tests
enabled_tests = [test for test in suite.tests if test.enabled]
if not enabled_tests:
self.logger.warning(f"No enabled tests in suite: {suite.name}")
return
# Notify reporters of suite start
for reporter in self.reporters:
if hasattr(reporter, 'report_layer_start'):
await reporter.report_layer_start(suite_index, len(enabled_tests))
# Execute suite setup if defined
if hasattr(suite, 'setup') and suite.setup:
await self._execute_suite_setup(suite)
# Execute tests sequentially
suite_results = []
for test_index, test in enumerate(enabled_tests):
if self._cancellation_requested:
break
self.logger.debug(f"Executing test {test_index + 1}/{len(enabled_tests)}: {test.name}")
self._current_test = test.name
# Check test dependencies
if not await self._check_test_dependencies(test):
self.logger.warning(f"Skipping test '{test.name}' due to unmet dependencies")
self._execution_stats.skipped += 1
continue
# Execute the test
result = await self._execute_single_test(test, suite)
suite_results.append(result)
self._results.append(result)
# Update statistics
if result.success:
self._execution_stats.passed += 1
else:
self._execution_stats.failed += 1
self._execution_stats.add_error(
test.name,
result.error_message or "Unknown error",
result.metadata
)
self._execution_stats.test_times[test.name] = result.execution_time
# Notify reporters of test result
for reporter in self.reporters:
if hasattr(reporter, 'report_test_result'):
await reporter.report_test_result(result)
# Add small delay to prevent overwhelming the server
if test_index < len(enabled_tests) - 1: # Don't delay after the last test
await asyncio.sleep(0.01) # 10ms delay between tests
# Execute suite teardown if defined
if hasattr(suite, 'teardown') and suite.teardown:
await self._execute_suite_teardown(suite)
# Notify reporters of suite completion
for reporter in self.reporters:
if hasattr(reporter, 'report_layer_completion'):
await reporter.report_layer_completion(suite_index, suite_results)
self._current_test = None
async def _execute_single_test(self, test: TestCase, suite: TestSuite) -> TestResult:
"""Execute a single test case with comprehensive error handling"""
test_start_time = time.time()
# Notify reporters of test start
for reporter in self.reporters:
if hasattr(reporter, 'report_test_start'):
await reporter.report_test_start(test.name)
try:
# Determine which server to use for this test
server_config = self._select_server_for_test(test)
# Create and connect test client
test_client = MCPTestClient(
server_config=server_config,
enable_metrics=self.metrics is not None,
enable_logging=True
)
async with test_client.connect():
# Execute the specific test type
result = await self._execute_test_by_type(test, test_client)
# Validate results if expected results are defined
if hasattr(test, 'expected_result') and test.expected_result:
validation_success = await self._validate_test_result(result, test.expected_result)
if not validation_success:
result.success = False
result.error_message = "Result validation failed"
# Record metrics if available
if self.metrics:
self.metrics.record_test_execution(
test.name,
test.test_type,
result.execution_time,
result.success
)
return result
except asyncio.CancelledError:
self.logger.info(f"Test '{test.name}' was cancelled")
return TestResult(
test_name=test.name,
success=False,
execution_time=time.time() - test_start_time,
error_message="Test cancelled by user",
metadata={"cancelled": True}
)
except Exception as e:
execution_time = time.time() - test_start_time
error_msg = f"Test execution failed: {str(e)}"
self.logger.error(f"Test '{test.name}' failed: {error_msg}")
return TestResult(
test_name=test.name,
success=False,
execution_time=execution_time,
error_message=error_msg,
metadata={
"exception_type": type(e).__name__,
"suite_name": suite.name
}
)
async def _execute_test_by_type(self, test: TestCase, client: MCPTestClient) -> TestResult:
"""Execute test based on its type"""
if test.test_type == "ping":
return await client.ping(timeout=test.timeout)
elif test.test_type == "tool_call":
return await client.call_tool(
tool_name=test.target,
parameters=test.parameters or {},
timeout=test.timeout,
enable_cancellation=getattr(test, 'enable_cancellation', False),
enable_progress=getattr(test, 'enable_progress', False),
enable_sampling=getattr(test, 'enable_sampling', False),
sampling_rate=getattr(test, 'sampling_rate', 1.0)
)
elif test.test_type == "resource_read":
return await client.read_resource(
resource_uri=test.target,
timeout=test.timeout
)
elif test.test_type == "prompt_get":
return await client.get_prompt(
prompt_name=test.target,
arguments=test.parameters or {},
timeout=test.timeout
)
elif test.test_type == "notification":
# Placeholder for notification testing
return await self._execute_notification_test(test, client)
else:
raise ValueError(f"Unknown test type: {test.test_type}")
async def _execute_notification_test(self, test: TestCase, client: MCPTestClient) -> TestResult:
"""Execute notification-specific test"""
start_time = time.time()
try:
# This would need to be implemented based on FastMCP notification API
# For now, return a placeholder success result
return TestResult(
test_name=test.name,
success=True,
execution_time=time.time() - start_time,
metadata={"test_type": "notification", "placeholder": True}
)
except Exception as e:
return TestResult(
test_name=test.name,
success=False,
execution_time=time.time() - start_time,
error_message=f"Notification test failed: {str(e)}"
)
async def _check_test_dependencies(self, test: TestCase) -> bool:
"""Check if test dependencies are satisfied"""
if not hasattr(test, 'depends_on') or not test.depends_on:
return True # No dependencies
# Check if all dependent tests have passed
passed_test_names = {result.test_name for result in self._results if result.success}
for dependency in test.depends_on:
if dependency not in passed_test_names:
self.logger.warning(f"Dependency '{dependency}' not satisfied for test '{test.name}'")
return False
return True
async def _validate_test_result(self, result: TestResult, expected: Dict[str, Any]) -> bool:
"""Validate test result against expected values"""
if not result.response_data:
return False
try:
# Simple validation - can be extended for more complex scenarios
for key, expected_value in expected.items():
if key not in result.response_data:
self.logger.debug(f"Expected key '{key}' not found in response")
return False
actual_value = result.response_data[key]
if actual_value != expected_value:
self.logger.debug(f"Value mismatch for '{key}': expected {expected_value}, got {actual_value}")
return False
return True
except Exception as e:
self.logger.error(f"Result validation failed: {e}")
return False
def _select_server_for_test(self, test: TestCase) -> Any:
"""Select appropriate server configuration for test"""
# If test specifies a server, use that
if hasattr(test, 'server_name') and test.server_name:
for server in self.config.servers:
if server.name == test.server_name:
return server
self.logger.warning(f"Specified server '{test.server_name}' not found, using default")
# Use first available server (simple round-robin could be added)
if self.config.servers:
return self.config.servers[0]
raise RuntimeError("No servers configured for testing")
async def _execute_suite_setup(self, suite: TestSuite):
"""Execute test suite setup procedures"""
self.logger.info(f"Executing setup for suite: {suite.name}")
try:
# Placeholder for suite setup logic
# This would depend on the actual TestSuite implementation
if callable(suite.setup):
await suite.setup()
except Exception as e:
self.logger.error(f"Suite setup failed for '{suite.name}': {e}")
raise
async def _execute_suite_teardown(self, suite: TestSuite):
"""Execute test suite teardown procedures"""
self.logger.info(f"Executing teardown for suite: {suite.name}")
try:
# Placeholder for suite teardown logic
# This would depend on the actual TestSuite implementation
if callable(suite.teardown):
await suite.teardown()
except Exception as e:
self.logger.error(f"Suite teardown failed for '{suite.name}': {e}")
# Don't raise here - teardown failures shouldn't stop execution
async def cancel_execution(self):
"""Cancel ongoing test execution gracefully"""
self.logger.info("Cancellation requested for sequential test execution")
self._cancellation_requested = True
if self._current_test:
self.logger.info(f"Will cancel after current test completes: {self._current_test}")
# Add to statistics
self._execution_stats.cancelled = 1
async def _cleanup_resources(self):
"""Cleanup resources and connections"""
self.logger.debug("Cleaning up sequential test runner resources")
try:
# Execute any registered cleanup tasks
for cleanup_task in self._cleanup_tasks:
try:
if asyncio.iscoroutinefunction(cleanup_task):
await cleanup_task()
else:
cleanup_task()
except Exception as e:
self.logger.warning(f"Cleanup task failed: {e}")
# Clear cleanup tasks
self._cleanup_tasks.clear()
self.logger.debug("Resource cleanup completed")
except Exception as e:
self.logger.error(f"Error during resource cleanup: {e}")
def add_cleanup_task(self, task: Any):
"""Add a cleanup task to be executed during resource cleanup"""
self._cleanup_tasks.append(task)
@property
def results(self) -> List[TestResult]:
"""Get all test results"""
return self._results
@property
def execution_stats(self) -> SequentialExecutionStats:
"""Get execution statistics"""
return self._execution_stats
@property
def is_running(self) -> bool:
"""Check if runner is currently executing tests"""
return self._current_test is not None
@property
def current_test(self) -> Optional[str]:
"""Get name of currently executing test"""
return self._current_test
def get_suite_statistics(self) -> Dict[str, Dict[str, Any]]:
"""Get detailed statistics by test suite"""
suite_stats = {}
for suite in self.config.test_suites:
suite_results = [r for r in self._results if r.metadata.get('suite_name') == suite.name]
if suite_results:
suite_stats[suite.name] = {
'total_tests': len(suite_results),
'passed': len([r for r in suite_results if r.success]),
'failed': len([r for r in suite_results if not r.success]),
'total_time': sum(r.execution_time for r in suite_results),
'average_time': sum(r.execution_time for r in suite_results) / len(suite_results),
'success_rate': (len([r for r in suite_results if r.success]) / len(suite_results)) * 100
}
return suite_stats
def get_performance_summary(self) -> Dict[str, Any]:
"""Get performance summary statistics"""
if not self._results:
return {}
execution_times = [r.execution_time for r in self._results]
return {
'total_execution_time': self._execution_stats.execution_time,
'average_test_time': sum(execution_times) / len(execution_times),
'fastest_test_time': min(execution_times),
'slowest_test_time': max(execution_times),
'tests_per_second': len(self._results) / self._execution_stats.execution_time if self._execution_stats.execution_time > 0 else 0,
'total_tests': len(self._results),
'success_rate': self._execution_stats.success_rate
}

View File

@ -0,0 +1,18 @@
"""
MCPTesta Utilities
Utility modules for logging, validation, metrics collection,
and other supporting functionality.
"""
from .logging import setup_logging, get_logger
from .validation import validate_yaml_schema, ConfigurationValidator
from .metrics import MetricsCollector
__all__ = [
"setup_logging",
"get_logger",
"validate_yaml_schema",
"ConfigurationValidator",
"MetricsCollector",
]

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,893 @@
"""
MCPTesta Metrics Collection
Performance metrics collection, aggregation, and reporting for comprehensive
test execution analysis and optimization with system resource monitoring.
"""
import time
import asyncio
import psutil
import threading
from datetime import datetime, timedelta
from typing import Dict, Any, List, Optional, Union
from dataclasses import dataclass, field
from collections import defaultdict, deque
import statistics
from contextlib import contextmanager, asynccontextmanager
from .logging import get_logger, LogContext
@dataclass
class MetricPoint:
"""Individual metric data point"""
timestamp: datetime
value: float
labels: Dict[str, str] = field(default_factory=dict)
@dataclass
class SystemResourceSnapshot:
"""System resource usage snapshot"""
timestamp: datetime
memory_mb: float
memory_percent: float
cpu_percent: float
disk_io_read_mb: float
disk_io_write_mb: float
network_bytes_sent: int
network_bytes_recv: int
active_threads: int
active_connections: int
@classmethod
def capture(cls, active_connections: int = 0) -> "SystemResourceSnapshot":
"""Capture current system resources"""
memory = psutil.virtual_memory()
cpu_percent = psutil.cpu_percent()
disk_io = psutil.disk_io_counters()
net_io = psutil.net_io_counters()
return cls(
timestamp=datetime.now(),
memory_mb=memory.used / (1024 * 1024),
memory_percent=memory.percent,
cpu_percent=cpu_percent,
disk_io_read_mb=(disk_io.read_bytes / (1024 * 1024)) if disk_io else 0,
disk_io_write_mb=(disk_io.write_bytes / (1024 * 1024)) if disk_io else 0,
network_bytes_sent=net_io.bytes_sent if net_io else 0,
network_bytes_recv=net_io.bytes_recv if net_io else 0,
active_threads=threading.active_count(),
active_connections=active_connections
)
@dataclass
class ConnectionPerformance:
"""Connection-specific performance metrics"""
server_name: str
transport_type: str
establishment_time: float
total_operations: int = 0
successful_operations: int = 0
failed_operations: int = 0
average_latency: float = 0.0
last_used: datetime = field(default_factory=datetime.now)
@property
def success_rate(self) -> float:
"""Calculate operation success rate"""
if self.total_operations == 0:
return 0.0
return (self.successful_operations / self.total_operations) * 100
@dataclass
class PerformanceStats:
"""Performance statistics for a specific operation type"""
operation_type: str
total_calls: int = 0
successful_calls: int = 0
failed_calls: int = 0
total_time: float = 0.0
min_time: float = float('inf')
max_time: float = 0.0
times: List[float] = field(default_factory=list)
# Enhanced performance tracking
error_types: Dict[str, int] = field(default_factory=dict)
latency_buckets: Dict[str, int] = field(default_factory=dict)
def __post_init__(self):
"""Initialize latency buckets"""
if not self.latency_buckets:
self.latency_buckets = {
"0-100ms": 0,
"100-500ms": 0,
"500ms-1s": 0,
"1s-5s": 0,
"5s+": 0
}
@property
def success_rate(self) -> float:
"""Calculate success rate percentage"""
if self.total_calls == 0:
return 0.0
return (self.successful_calls / self.total_calls) * 100
@property
def average_time(self) -> float:
"""Calculate average execution time"""
if not self.times:
return 0.0
return statistics.mean(self.times)
@property
def median_time(self) -> float:
"""Calculate median execution time"""
if not self.times:
return 0.0
return statistics.median(self.times)
@property
def percentile_95(self) -> float:
"""Calculate 95th percentile execution time"""
if not self.times:
return 0.0
sorted_times = sorted(self.times)
index = int(0.95 * len(sorted_times))
return sorted_times[index] if index < len(sorted_times) else sorted_times[-1]
@property
def percentile_99(self) -> float:
"""Calculate 99th percentile execution time"""
if not self.times:
return 0.0
sorted_times = sorted(self.times)
index = int(0.99 * len(sorted_times))
return sorted_times[index] if index < len(sorted_times) else sorted_times[-1]
def update_latency_bucket(self, execution_time: float):
"""Update latency distribution buckets"""
if execution_time < 0.1:
self.latency_buckets["0-100ms"] += 1
elif execution_time < 0.5:
self.latency_buckets["100-500ms"] += 1
elif execution_time < 1.0:
self.latency_buckets["500ms-1s"] += 1
elif execution_time < 5.0:
self.latency_buckets["1s-5s"] += 1
else:
self.latency_buckets["5s+"] += 1
def record_error(self, error_type: str):
"""Record error type for analysis"""
self.error_types[error_type] = self.error_types.get(error_type, 0) + 1
class MetricsCollector:
"""
Comprehensive metrics collection system for MCPTesta.
Collects and aggregates performance metrics, connection statistics,
resource utilization, and test execution data with system monitoring.
"""
def __init__(self, max_history: int = 10000, enable_system_monitoring: bool = True):
self.max_history = max_history
self.enable_system_monitoring = enable_system_monitoring
self.logger = get_logger(__name__)
# Time series data
self.metrics: Dict[str, deque] = defaultdict(lambda: deque(maxlen=max_history))
# Performance statistics
self.performance_stats: Dict[str, PerformanceStats] = {}
# Connection tracking with enhanced performance metrics
self.connection_metrics = {
'total_connections': 0,
'successful_connections': 0,
'failed_connections': 0,
'connection_times': deque(maxlen=1000),
'connection_pool_hits': 0,
'connection_pool_misses': 0,
}
# Connection performance tracking
self.connection_performance: Dict[str, ConnectionPerformance] = {}
# System resource monitoring
self.resource_snapshots: deque = deque(maxlen=1000)
self.resource_metrics = {
'peak_memory_mb': 0,
'current_memory_mb': 0,
'peak_memory_percent': 0,
'cpu_usage_percent': 0,
'peak_cpu_percent': 0,
'active_connections': 0,
'peak_connections': 0,
'active_threads': 0,
'peak_threads': 0,
'disk_io_read_mb': 0,
'disk_io_write_mb': 0,
'network_bytes_sent': 0,
'network_bytes_recv': 0,
}
# Test execution metrics
self.test_metrics = {
'total_tests': 0,
'passed_tests': 0,
'failed_tests': 0,
'skipped_tests': 0,
'cancelled_tests': 0,
'execution_times': deque(maxlen=10000),
}
# Timeline-based metrics for trend analysis
self.timeline_metrics: Dict[str, List[Dict[str, Any]]] = defaultdict(list)
# Start time and baseline measurements
self.start_time = datetime.now()
self.baseline_snapshot: Optional[SystemResourceSnapshot] = None
# System monitoring task
self._monitoring_task: Optional[asyncio.Task] = None
self._monitoring_enabled = False
# Capture baseline if system monitoring enabled
if self.enable_system_monitoring:
try:
self.baseline_snapshot = SystemResourceSnapshot.capture()
self.logger.debug("Baseline system snapshot captured")
except Exception as e:
self.logger.warning(f"Failed to capture baseline snapshot: {e}")
self.enable_system_monitoring = False
def record_metric(self, name: str, value: float, labels: Optional[Dict[str, str]] = None):
"""Record a generic metric point"""
metric_point = MetricPoint(
timestamp=datetime.now(),
value=value,
labels=labels or {}
)
self.metrics[name].append(metric_point)
self.logger.debug(f"Recorded metric {name}: {value}")
def record_connection_time(self, connection_time: float):
"""Record connection establishment time"""
self.connection_metrics['total_connections'] += 1
self.connection_metrics['connection_times'].append(connection_time)
if connection_time > 0:
self.connection_metrics['successful_connections'] += 1
else:
self.connection_metrics['failed_connections'] += 1
self.record_metric('connection_time', connection_time)
async def start_monitoring(self, interval: float = 5.0):
"""Start system resource monitoring"""
if not self.enable_system_monitoring or self._monitoring_enabled:
return
self._monitoring_enabled = True
self._monitoring_task = asyncio.create_task(self._monitor_resources(interval))
self.logger.info("System resource monitoring started")
async def stop_monitoring(self):
"""Stop system resource monitoring"""
self._monitoring_enabled = False
if self._monitoring_task:
self._monitoring_task.cancel()
try:
await self._monitoring_task
except asyncio.CancelledError:
pass
self._monitoring_task = None
self.logger.info("System resource monitoring stopped")
async def _monitor_resources(self, interval: float):
"""Monitor system resources periodically"""
while self._monitoring_enabled:
try:
snapshot = SystemResourceSnapshot.capture(
active_connections=self.resource_metrics['active_connections']
)
self.resource_snapshots.append(snapshot)
# Update peak values
self.resource_metrics['current_memory_mb'] = snapshot.memory_mb
self.resource_metrics['peak_memory_mb'] = max(
self.resource_metrics['peak_memory_mb'],
snapshot.memory_mb
)
self.resource_metrics['peak_memory_percent'] = max(
self.resource_metrics['peak_memory_percent'],
snapshot.memory_percent
)
self.resource_metrics['cpu_usage_percent'] = snapshot.cpu_percent
self.resource_metrics['peak_cpu_percent'] = max(
self.resource_metrics['peak_cpu_percent'],
snapshot.cpu_percent
)
self.resource_metrics['active_threads'] = snapshot.active_threads
self.resource_metrics['peak_threads'] = max(
self.resource_metrics['peak_threads'],
snapshot.active_threads
)
# Record as time series metrics
self.record_metric('memory_usage_mb', snapshot.memory_mb)
self.record_metric('memory_usage_percent', snapshot.memory_percent)
self.record_metric('cpu_usage_percent', snapshot.cpu_percent)
self.record_metric('active_threads', snapshot.active_threads)
self.record_metric('disk_io_read_mb', snapshot.disk_io_read_mb)
self.record_metric('disk_io_write_mb', snapshot.disk_io_write_mb)
await asyncio.sleep(interval)
except asyncio.CancelledError:
break
except Exception as e:
self.logger.warning(f"Error in resource monitoring: {e}")
await asyncio.sleep(interval)
def record_connection_performance(self, server_name: str, transport_type: str,
establishment_time: float, success: bool = True):
"""Record connection establishment performance"""
connection_key = f"{server_name}_{transport_type}"
if connection_key not in self.connection_performance:
self.connection_performance[connection_key] = ConnectionPerformance(
server_name=server_name,
transport_type=transport_type,
establishment_time=establishment_time
)
conn_perf = self.connection_performance[connection_key]
if success:
conn_perf.successful_operations += 1
else:
conn_perf.failed_operations += 1
conn_perf.total_operations += 1
conn_perf.last_used = datetime.now()
self.record_connection_time(establishment_time)
def record_connection_pool_event(self, event_type: str):
"""Record connection pool events (hit/miss)"""
if event_type == "hit":
self.connection_metrics['connection_pool_hits'] += 1
elif event_type == "miss":
self.connection_metrics['connection_pool_misses'] += 1
self.record_metric('connection_pool_event', 1.0, {'type': event_type})
def record_tool_call(self, tool_name: str, execution_time: float, success: bool,
error_type: Optional[str] = None):
"""Record tool call performance with enhanced error tracking"""
operation_type = f"tool_call_{tool_name}"
# Initialize stats if not exists
if operation_type not in self.performance_stats:
self.performance_stats[operation_type] = PerformanceStats(operation_type)
stats = self.performance_stats[operation_type]
stats.total_calls += 1
if success:
stats.successful_calls += 1
else:
stats.failed_calls += 1
if error_type:
stats.record_error(error_type)
# Update timing statistics
stats.total_time += execution_time
stats.min_time = min(stats.min_time, execution_time)
stats.max_time = max(stats.max_time, execution_time)
stats.times.append(execution_time)
stats.update_latency_bucket(execution_time)
# Record as time series
labels = {
'tool': tool_name,
'success': str(success)
}
if error_type:
labels['error_type'] = error_type
self.record_metric('tool_call_time', execution_time, labels)
# Add to timeline for trend analysis
self.timeline_metrics['tool_calls'].append({
'timestamp': datetime.now().isoformat(),
'tool_name': tool_name,
'execution_time': execution_time,
'success': success,
'error_type': error_type
})
self.logger.debug(f"Recorded tool call {tool_name}: {execution_time:.3f}s ({'success' if success else 'failure'})")
def record_resource_read(self, resource_uri: str, execution_time: float, success: bool):
"""Record resource read performance"""
operation_type = "resource_read"
if operation_type not in self.performance_stats:
self.performance_stats[operation_type] = PerformanceStats(operation_type)
stats = self.performance_stats[operation_type]
stats.total_calls += 1
if success:
stats.successful_calls += 1
else:
stats.failed_calls += 1
stats.total_time += execution_time
stats.min_time = min(stats.min_time, execution_time)
stats.max_time = max(stats.max_time, execution_time)
stats.times.append(execution_time)
self.record_metric('resource_read_time', execution_time, {
'resource': resource_uri,
'success': str(success)
})
def record_prompt_get(self, prompt_name: str, execution_time: float, success: bool):
"""Record prompt get performance"""
operation_type = "prompt_get"
if operation_type not in self.performance_stats:
self.performance_stats[operation_type] = PerformanceStats(operation_type)
stats = self.performance_stats[operation_type]
stats.total_calls += 1
if success:
stats.successful_calls += 1
else:
stats.failed_calls += 1
stats.total_time += execution_time
stats.min_time = min(stats.min_time, execution_time)
stats.max_time = max(stats.max_time, execution_time)
stats.times.append(execution_time)
self.record_metric('prompt_get_time', execution_time, {
'prompt': prompt_name,
'success': str(success)
})
def record_test_result(self, execution_time: float, success: bool, skipped: bool = False):
"""Record test execution result"""
self.test_metrics['total_tests'] += 1
if skipped:
self.test_metrics['skipped_tests'] += 1
elif success:
self.test_metrics['passed_tests'] += 1
else:
self.test_metrics['failed_tests'] += 1
self.test_metrics['execution_times'].append(execution_time)
self.record_metric('test_execution_time', execution_time, {
'success': str(success),
'skipped': str(skipped)
})
def update_resource_usage(self, memory_mb: float, cpu_percent: float, active_connections: int):
"""Update current resource usage"""
self.resource_metrics['current_memory_mb'] = memory_mb
self.resource_metrics['peak_memory_mb'] = max(
self.resource_metrics['peak_memory_mb'],
memory_mb
)
self.resource_metrics['cpu_usage_percent'] = cpu_percent
self.resource_metrics['active_connections'] = active_connections
# Record as time series
self.record_metric('memory_usage_mb', memory_mb)
self.record_metric('cpu_usage_percent', cpu_percent)
self.record_metric('active_connections', active_connections)
def get_summary_stats(self) -> Dict[str, Any]:
"""Get comprehensive summary statistics"""
now = datetime.now()
duration = now - self.start_time
# Calculate overall statistics
all_times = list(self.test_metrics['execution_times'])
summary = {
'session': {
'duration_seconds': duration.total_seconds(),
'start_time': self.start_time.isoformat(),
'end_time': now.isoformat(),
},
'connections': {
'total': self.connection_metrics['total_connections'],
'successful': self.connection_metrics['successful_connections'],
'failed': self.connection_metrics['failed_connections'],
'success_rate': (
self.connection_metrics['successful_connections'] /
max(self.connection_metrics['total_connections'], 1) * 100
),
'average_time': (
statistics.mean(self.connection_metrics['connection_times'])
if self.connection_metrics['connection_times'] else 0
),
},
'tests': {
'total': self.test_metrics['total_tests'],
'passed': self.test_metrics['passed_tests'],
'failed': self.test_metrics['failed_tests'],
'skipped': self.test_metrics['skipped_tests'],
'success_rate': (
self.test_metrics['passed_tests'] /
max(self.test_metrics['total_tests'], 1) * 100
),
'average_time': statistics.mean(all_times) if all_times else 0,
'median_time': statistics.median(all_times) if all_times else 0,
'total_time': sum(all_times),
},
'resources': self.resource_metrics.copy(),
'performance': {}
}
# Add performance statistics for each operation type
for op_type, stats in self.performance_stats.items():
summary['performance'][op_type] = {
'total_calls': stats.total_calls,
'success_rate': stats.success_rate,
'average_time': stats.average_time,
'median_time': stats.median_time,
'min_time': stats.min_time if stats.min_time != float('inf') else 0,
'max_time': stats.max_time,
'p95_time': stats.percentile_95,
'total_time': stats.total_time,
}
return summary
def get_time_series(self, metric_name: str,
since: Optional[datetime] = None,
labels: Optional[Dict[str, str]] = None) -> List[MetricPoint]:
"""Get time series data for a specific metric"""
if metric_name not in self.metrics:
return []
points = list(self.metrics[metric_name])
# Filter by time if specified
if since:
points = [p for p in points if p.timestamp >= since]
# Filter by labels if specified
if labels:
points = [
p for p in points
if all(p.labels.get(k) == v for k, v in labels.items())
]
return points
def export_metrics(self, format: str = "dict") -> Any:
"""Export all metrics in specified format"""
summary = self.get_summary_stats()
if format == "dict":
return summary
elif format == "json":
import json
return json.dumps(summary, indent=2, default=str)
elif format == "csv":
# Export time series data as CSV
import io
import csv
output = io.StringIO()
writer = csv.writer(output)
# Write header
writer.writerow(['timestamp', 'metric', 'value', 'labels'])
# Write data points
for metric_name, points in self.metrics.items():
for point in points:
labels_str = ','.join(f"{k}={v}" for k, v in point.labels.items())
writer.writerow([
point.timestamp.isoformat(),
metric_name,
point.value,
labels_str
])
return output.getvalue()
else:
raise ValueError(f"Unsupported export format: {format}")
def reset_metrics(self):
"""Reset all metrics and statistics"""
self.metrics.clear()
self.performance_stats.clear()
# Reset connection metrics
self.connection_metrics = {
'total_connections': 0,
'successful_connections': 0,
'failed_connections': 0,
'connection_times': deque(maxlen=1000),
}
# Reset resource metrics
self.resource_metrics = {
'peak_memory_mb': 0,
'current_memory_mb': 0,
'cpu_usage_percent': 0,
'active_connections': 0,
}
# Reset test metrics
self.test_metrics = {
'total_tests': 0,
'passed_tests': 0,
'failed_tests': 0,
'skipped_tests': 0,
'execution_times': deque(maxlen=10000),
}
self.start_time = datetime.now()
self.logger.info("Metrics have been reset")
# Global metrics collector instance
_global_metrics = MetricsCollector()
def get_global_metrics() -> MetricsCollector:
"""Get the global metrics collector instance"""
return _global_metrics
def record_metric(name: str, value: float, labels: Optional[Dict[str, str]] = None):
"""Record a metric using the global collector"""
_global_metrics.record_metric(name, value, labels)
def get_summary_stats() -> Dict[str, Any]:
"""Get summary statistics from global collector"""
return _global_metrics.get_summary_stats()
# Context managers for automatic metrics collection
@contextmanager
def operation_timer(operation_name: str, labels: Optional[Dict[str, str]] = None):
"""Context manager for timing operations"""
start_time = time.time()
try:
yield
except Exception as e:
execution_time = time.time() - start_time
error_labels = (labels or {}).copy()
error_labels.update({'success': 'false', 'error': type(e).__name__})
record_metric(f'{operation_name}_time', execution_time, error_labels)
raise
else:
execution_time = time.time() - start_time
success_labels = (labels or {}).copy()
success_labels.update({'success': 'true'})
record_metric(f'{operation_name}_time', execution_time, success_labels)
@asynccontextmanager
async def async_operation_timer(operation_name: str, labels: Optional[Dict[str, str]] = None):
"""Async context manager for timing operations"""
start_time = time.time()
try:
yield
except Exception as e:
execution_time = time.time() - start_time
error_labels = (labels or {}).copy()
error_labels.update({'success': 'false', 'error': type(e).__name__})
record_metric(f'{operation_name}_time', execution_time, error_labels)
raise
else:
execution_time = time.time() - start_time
success_labels = (labels or {}).copy()
success_labels.update({'success': 'true'})
record_metric(f'{operation_name}_time', execution_time, success_labels)
@contextmanager
def resource_monitor(collector: Optional[MetricsCollector] = None):
"""Context manager for monitoring resource usage during operation"""
if collector is None:
collector = _global_metrics
if not collector.enable_system_monitoring:
yield
return
try:
start_snapshot = SystemResourceSnapshot.capture()
yield
end_snapshot = SystemResourceSnapshot.capture()
# Calculate resource deltas
memory_delta = end_snapshot.memory_mb - start_snapshot.memory_mb
cpu_time = end_snapshot.cpu_percent # Instantaneous measurement
collector.record_metric('operation_memory_delta', memory_delta)
collector.record_metric('operation_cpu_usage', cpu_time)
except Exception as e:
collector.logger.warning(f"Error in resource monitoring: {e}")
yield
@asynccontextmanager
async def metrics_session(collector: Optional[MetricsCollector] = None,
monitoring_interval: float = 5.0):
"""Context manager for comprehensive metrics collection during a session"""
if collector is None:
collector = _global_metrics
try:
# Start system monitoring
if collector.enable_system_monitoring:
await collector.start_monitoring(monitoring_interval)
with LogContext(session_id=f"metrics_{int(time.time())}"):
yield collector
finally:
# Stop monitoring
if collector.enable_system_monitoring:
await collector.stop_monitoring()
class MetricsContext:
"""Context manager for scoped metrics collection"""
def __init__(self, scope: str, collector: Optional[MetricsCollector] = None):
self.scope = scope
self.collector = collector or _global_metrics
self.start_time = None
self.start_snapshot = None
def __enter__(self):
self.start_time = time.time()
if self.collector.enable_system_monitoring:
try:
self.start_snapshot = SystemResourceSnapshot.capture()
except Exception as e:
self.collector.logger.warning(f"Failed to capture start snapshot: {e}")
return self
def __exit__(self, exc_type, exc_val, exc_tb):
duration = time.time() - self.start_time
# Record operation duration
labels = {'scope': self.scope}
if exc_type:
labels['success'] = 'false'
labels['error'] = exc_type.__name__
else:
labels['success'] = 'true'
self.collector.record_metric('scoped_operation_time', duration, labels)
# Record resource usage if available
if self.start_snapshot and self.collector.enable_system_monitoring:
try:
end_snapshot = SystemResourceSnapshot.capture()
memory_delta = end_snapshot.memory_mb - self.start_snapshot.memory_mb
self.collector.record_metric('scoped_memory_delta', memory_delta, labels)
except Exception as e:
self.collector.logger.warning(f"Failed to capture end snapshot: {e}")
# Performance analysis utilities
def analyze_performance_trends(collector: MetricsCollector,
operation_type: str,
window_minutes: int = 30) -> Dict[str, Any]:
"""Analyze performance trends for an operation type"""
since = datetime.now() - timedelta(minutes=window_minutes)
# Get recent metrics
recent_points = collector.get_time_series(f'{operation_type}_time', since=since)
if not recent_points:
return {"error": "No data available for analysis"}
times = [point.value for point in recent_points]
timestamps = [point.timestamp for point in recent_points]
# Calculate trend metrics
analysis = {
"operation_type": operation_type,
"window_minutes": window_minutes,
"sample_count": len(times),
"average_time": statistics.mean(times),
"median_time": statistics.median(times),
"min_time": min(times),
"max_time": max(times),
"std_dev": statistics.stdev(times) if len(times) > 1 else 0,
}
# Calculate percentiles
if len(times) >= 10:
sorted_times = sorted(times)
analysis["p50"] = sorted_times[int(0.5 * len(sorted_times))]
analysis["p95"] = sorted_times[int(0.95 * len(sorted_times))]
analysis["p99"] = sorted_times[int(0.99 * len(sorted_times))]
# Trend analysis (simple linear regression)
if len(times) >= 5:
x_values = list(range(len(times)))
x_mean = statistics.mean(x_values)
y_mean = statistics.mean(times)
numerator = sum((x - x_mean) * (y - y_mean) for x, y in zip(x_values, times))
denominator = sum((x - x_mean) ** 2 for x in x_values)
if denominator != 0:
slope = numerator / denominator
analysis["trend_slope"] = slope
analysis["trend_direction"] = "improving" if slope < 0 else "degrading" if slope > 0 else "stable"
return analysis
def generate_performance_report(collector: MetricsCollector) -> str:
"""Generate a comprehensive performance report"""
summary = collector.get_summary_stats()
report_lines = [
"=" * 60,
"MCPTesta Performance Report",
"=" * 60,
f"Report Generated: {datetime.now().isoformat()}",
f"Session Duration: {summary['session']['duration_seconds']:.2f} seconds",
"",
"CONNECTION METRICS:",
f" Total Connections: {summary['connections']['total']}",
f" Success Rate: {summary['connections']['success_rate']:.1f}%",
f" Average Connection Time: {summary['connections']['average_time']:.3f}s",
"",
"TEST EXECUTION:",
f" Total Tests: {summary['tests']['total']}",
f" Success Rate: {summary['tests']['success_rate']:.1f}%",
f" Average Execution Time: {summary['tests']['average_time']:.3f}s",
f" Median Execution Time: {summary['tests']['median_time']:.3f}s",
"",
"SYSTEM RESOURCES:",
f" Peak Memory Usage: {summary['resources']['peak_memory_mb']:.1f} MB",
f" Peak CPU Usage: {summary['resources']['peak_cpu_percent']:.1f}%",
f" Peak Connections: {summary['resources']['peak_connections']}",
f" Peak Threads: {summary['resources']['peak_threads']}",
]
# Add performance breakdown
if summary['performance']:
report_lines.extend([
"",
"OPERATION PERFORMANCE:",
])
for op_type, stats in summary['performance'].items():
report_lines.extend([
f" {op_type}:",
f" Calls: {stats['total_calls']}",
f" Success Rate: {stats['success_rate']:.1f}%",
f" Avg Time: {stats['average_time']:.3f}s",
f" P95 Time: {stats['p95_time']:.3f}s",
])
report_lines.append("=" * 60)
return "\n".join(report_lines)

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,15 @@
"""
MCPTesta YAML Parser
YAML configuration parsing and template generation for MCPTesta.
"""
from .parser import YAMLTestParser, TestCase, TestSuite
from .templates import ConfigTemplateGenerator
__all__ = [
"YAMLTestParser",
"TestCase",
"TestSuite",
"ConfigTemplateGenerator",
]

View File

@ -0,0 +1,278 @@
"""
YAML Test Configuration Parser
Parses YAML test configuration files for comprehensive FastMCP server testing.
Supports complex test scenarios, parallel execution, and advanced MCP features.
"""
import re
import yaml
from pathlib import Path
from typing import Dict, Any, List, Optional, Union
from pydantic import BaseModel, validator, Field
from dataclasses import dataclass
from ..core.config import TestConfig, ServerConfig
from ..utils.validation import validate_yaml_schema
class YAMLParseError(Exception):
"""Raised when YAML parsing fails"""
pass
@dataclass
class TestCase:
"""Individual test case configuration"""
name: str
description: Optional[str] = None
enabled: bool = True
tags: List[str] = None
timeout: int = 30
retry_count: int = 0
depends_on: List[str] = None
# Test type and parameters
test_type: str = "tool_call" # tool_call, resource_read, prompt_get, notification, ping
target: str = "" # tool name, resource URI, prompt name
parameters: Dict[str, Any] = None
expected_result: Dict[str, Any] = None
expected_error: Optional[str] = None
# Advanced features
enable_cancellation: bool = False
enable_progress: bool = False
enable_sampling: bool = False
sampling_rate: float = 1.0
def __post_init__(self):
if self.tags is None:
self.tags = []
if self.parameters is None:
self.parameters = {}
if self.expected_result is None:
self.expected_result = {}
@dataclass
class TestSuite:
"""Test suite containing multiple related tests"""
name: str
description: Optional[str] = None
enabled: bool = True
tags: List[str] = None
setup: Dict[str, Any] = None
teardown: Dict[str, Any] = None
timeout: int = 300
parallel: bool = True
tests: List[TestCase] = None
def __post_init__(self):
if self.tags is None:
self.tags = []
if self.setup is None:
self.setup = {}
if self.teardown is None:
self.teardown = {}
if self.tests is None:
self.tests = []
class YAMLTestParser:
"""
Parser for YAML test configuration files.
Supports comprehensive test scenarios including:
- Tool testing with parameters and validation
- Resource reading and content validation
- Prompt generation and template testing
- Notification system testing
- Advanced MCP features (cancellation, progress, sampling)
- Parallel execution and dependency management
"""
def __init__(self):
self.schema_validator = validate_yaml_schema
def parse_file(self, config_path: Path) -> TestConfig:
"""Parse YAML test configuration file"""
try:
with open(config_path, 'r', encoding='utf-8') as f:
yaml_content = yaml.safe_load(f)
return self.parse_dict(yaml_content)
except FileNotFoundError:
raise YAMLParseError(f"Configuration file not found: {config_path}")
except yaml.YAMLError as e:
raise YAMLParseError(f"YAML syntax error: {e}")
except Exception as e:
raise YAMLParseError(f"Failed to parse configuration: {e}")
def parse_dict(self, config_data: Dict[str, Any]) -> TestConfig:
"""Parse configuration from dictionary"""
# Validate schema
self.schema_validator(config_data)
# Parse servers
servers = []
for server_data in config_data.get("servers", []):
server_config = self._parse_server_config(server_data)
servers.append(server_config)
if not servers:
raise YAMLParseError("At least one server must be configured")
# Parse test suites
test_suites = []
for suite_data in config_data.get("test_suites", []):
test_suite = self._parse_test_suite(suite_data)
test_suites.append(test_suite)
if not test_suites:
raise YAMLParseError("At least one test suite must be configured")
# Parse global configuration
global_config = config_data.get("config", {})
return TestConfig(
servers=servers,
test_suites=test_suites,
parallel_workers=global_config.get("parallel_workers", 4),
output_directory=global_config.get("output_directory"),
output_format=global_config.get("output_format", "console"),
include_tools=global_config.get("include_tools"),
exclude_tools=global_config.get("exclude_tools"),
features=global_config.get("features", {}),
max_concurrent_operations=global_config.get("max_concurrent_operations", 10),
enable_stress_testing=global_config.get("enable_stress_testing", False),
enable_memory_profiling=global_config.get("enable_memory_profiling", False),
enable_performance_profiling=global_config.get("enable_performance_profiling", False),
global_timeout=global_config.get("global_timeout", 300),
retry_policy=global_config.get("retry_policy", {}),
notification_config=global_config.get("notifications", {}),
)
def _parse_server_config(self, server_data: Dict[str, Any]) -> ServerConfig:
"""Parse server configuration"""
return ServerConfig(
name=server_data.get("name", "unnamed"),
command=server_data["command"],
transport=server_data.get("transport", "stdio"),
timeout=server_data.get("timeout", 30),
env_vars=server_data.get("env_vars", {}),
working_directory=server_data.get("working_directory"),
auth_token=server_data.get("auth_token"),
auth_type=server_data.get("auth_type", "bearer"),
headers=server_data.get("headers", {}),
enabled=server_data.get("enabled", True),
)
def _parse_test_suite(self, suite_data: Dict[str, Any]) -> TestSuite:
"""Parse test suite configuration"""
# Parse individual tests
tests = []
for test_data in suite_data.get("tests", []):
test_case = self._parse_test_case(test_data)
tests.append(test_case)
return TestSuite(
name=suite_data["name"],
description=suite_data.get("description"),
enabled=suite_data.get("enabled", True),
tags=suite_data.get("tags", []),
setup=suite_data.get("setup", {}),
teardown=suite_data.get("teardown", {}),
timeout=suite_data.get("timeout", 300),
parallel=suite_data.get("parallel", True),
tests=tests,
)
def _parse_test_case(self, test_data: Dict[str, Any]) -> TestCase:
"""Parse individual test case"""
return TestCase(
name=test_data["name"],
description=test_data.get("description"),
enabled=test_data.get("enabled", True),
tags=test_data.get("tags", []),
timeout=test_data.get("timeout", 30),
retry_count=test_data.get("retry_count", 0),
depends_on=test_data.get("depends_on", []),
test_type=test_data.get("test_type", "tool_call"),
target=test_data["target"],
parameters=test_data.get("parameters", {}),
expected_result=test_data.get("expected", {}),
expected_error=test_data.get("expected_error"),
enable_cancellation=test_data.get("enable_cancellation", False),
enable_progress=test_data.get("enable_progress", False),
enable_sampling=test_data.get("enable_sampling", False),
sampling_rate=test_data.get("sampling_rate", 1.0),
)
def parse_directory(self, directory: Path) -> List[TestConfig]:
"""Parse all YAML files in a directory"""
configs = []
yaml_files = directory.glob("*.yaml") + directory.glob("*.yml")
for yaml_file in sorted(yaml_files):
try:
config = self.parse_file(yaml_file)
configs.append(config)
except YAMLParseError as e:
print(f"Warning: Skipped {yaml_file}: {e}")
return configs
def validate_dependencies(self, test_suites: List[TestSuite]) -> List[str]:
"""Validate test dependencies and return any issues"""
issues = []
all_test_names = set()
# Collect all test names
for suite in test_suites:
for test in suite.tests:
if test.name in all_test_names:
issues.append(f"Duplicate test name: {test.name}")
all_test_names.add(test.name)
# Validate dependencies
for suite in test_suites:
for test in suite.tests:
for dependency in test.depends_on:
if dependency not in all_test_names:
issues.append(f"Test '{test.name}' depends on unknown test '{dependency}'")
return issues
def resolve_variables(self, config_data: Dict[str, Any], variables: Dict[str, str]) -> Dict[str, Any]:
"""Resolve variables in configuration using ${VAR} syntax"""
def replace_variables(obj):
if isinstance(obj, str):
# Replace ${VAR} and ${VAR:default} patterns
pattern = r'\$\{([^}:]+)(?::([^}]*))?\}'
def replacer(match):
var_name = match.group(1)
default_value = match.group(2) if match.group(2) is not None else ""
return variables.get(var_name, default_value)
return re.sub(pattern, replacer, obj)
elif isinstance(obj, dict):
return {k: replace_variables(v) for k, v in obj.items()}
elif isinstance(obj, list):
return [replace_variables(item) for item in obj]
else:
return obj
return replace_variables(config_data)

File diff suppressed because it is too large Load Diff