ExMCP Development Guide
View SourceThis guide covers everything you need to know for developing, testing, and contributing to ExMCP.
Table of Contents
Development Setup
Prerequisites
- Elixir 1.14+ and Erlang/OTP 25+
- Git with hooks support
Initial Setup
# Clone the repository
git clone https://github.com/azmaveth/ex_mcp.git
cd ex_mcp
# Install dependencies
mix deps.get
# Install git hooks
mix git_hooks.install
# Verify setup
mix compile --warnings-as-errors && mix credo
Essential Development Commands
# Dependencies and compilation
mix deps.get # Install dependencies
mix compile # Compile the project
mix compile --warnings-as-errors # Compile with strict warnings
# Code quality
mix format # Format code (required before committing)
mix credo # Static code analysis
mix dialyzer # Type checking (run after significant changes)
mix sobelow --skip # Security analysis
# Testing
mix test # Run all tests
mix test test/ex_mcp/protocol_test.exs # Run specific test file
mix coveralls.html # Generate coverage report
MIX_ENV=test mix compile # Compile for test environment
# Documentation
mix docs # Generate documentation
iex -S mix # Start interactive shell with project loaded
Code Quality Tools
ExMCP uses a comprehensive set of code quality tools to ensure maintainable, reliable code:
Formatter
- Tool: Elixir's built-in code formatter
- Usage:
mix format - Purpose: Consistent code formatting across the project
- Required: Yes, before every commit
Credo
- Tool: Static code analysis
- Usage:
mix credoormix credo --strict - Purpose: Code readability, consistency, and best practices
- Configuration: See
.credo.exs - Thresholds:
- Cyclomatic complexity max: 11
- Function length max: reasonable (enforced by review)
Dialyzer
- Tool: Type checking and static analysis
- Usage:
mix dialyzer - Purpose: Find type inconsistencies and potential runtime errors
- When to run: After significant changes or before releases
- PLT location:
_dialyzer/(gitignored)
Sobelow
- Tool: Security analysis
- Usage:
mix sobelow --skip - Purpose: Identify security vulnerabilities
- Focus: Input validation, SQL injection, XSS prevention
ExCoveralls
- Tool: Test coverage analysis
- Usage:
mix coveralls.html - Purpose: Ensure comprehensive test coverage
- Target: Aim for >80% coverage on core modules
Git Hooks
- Pre-commit: Runs formatter, credo, and compile checks
- Pre-push: Runs full test suite
- Setup: Automatically configured with
make setup - Bypass: Use
--no-verifyonly in emergencies
Testing Strategy
ExMCP uses a sophisticated test tagging strategy for efficient test execution across different scenarios.
Test Categories
Core Test Suites
# Fast unit tests (default, ~5s)
mix test.suite unit
# MCP specification compliance tests
mix test.suite compliance
# Integration tests with real components
mix test.suite integration
# CI-appropriate tests (excludes slow tests)
mix test.suite ci
# All tests including slow ones
mix test.suite all
Transport-Specific Tests
# BEAM transport tests
mix test --only beam
# HTTP transport tests (Streamable HTTP with SSE)
mix test --only http
# stdio transport tests
mix test --only stdio
Feature-Specific Tests
# Security and authentication tests
mix test --only security
# Progress notification tests
mix test --only progress
# Resource management tests
mix test --only resources
# Performance and benchmarking tests
mix test --only performance
Development Workflows
# Include normally excluded slow tests
mix test --include slow
# Skip integration tests for faster feedback
mix test --exclude integration
# Skip external dependencies
mix test --exclude external
# List all available tags
mix test.tags
Test Organization
Tests follow a clear structure mirroring the source code:
test/
├── ex_mcp/ # Core module tests
│ ├── client/ # Client implementation tests
│ ├── server/ # Server implementation tests
│ ├── transport/ # Transport layer tests
│ └── protocol_test.exs # Protocol tests
├── integration/ # Cross-component integration tests
├── compliance/ # MCP specification compliance tests
└── support/ # Test helpers and utilitiesTest Patterns
Unit Tests
- Test individual functions and modules in isolation
- Use mocks for external dependencies
- Fast execution (typically <100ms per test)
- Tagged with
:unit(default)
Integration Tests
- Test component interactions
- May use real external services or subprocess
- Slower execution
- Tagged with
:integration
Compliance Tests
- Verify MCP specification adherence
- Test protocol message formats
- Cross-version compatibility
- Tagged with
:compliance
Property-Based Tests
- Used for protocol encoding/decoding
- Input validation testing
- Edge case discovery
- Uses PropCheck library
Writing Tests
Follow these patterns when writing tests:
defmodule ExMCP.SomeModuleTest do
use ExUnit.Case, async: true # Use async: false for shared state
# Add appropriate tags
@moduletag :unit
@moduletag :some_feature
# Use descriptive test names
describe "function_name/2" do
test "handles valid input correctly" do
# Arrange
input = %{valid: "data"}
# Act
result = SomeModule.function_name(input, [])
# Assert
assert {:ok, expected} = result
end
test "returns error for invalid input" do
# Test error conditions
assert {:error, _reason} = SomeModule.function_name(nil, [])
end
end
endTest Process Cleanup
Tests that start servers can sometimes leave processes running if they crash. ExMCP provides several tools to clean up these stray processes:
Automatic Cleanup
# Clean up before running tests (automatic with make test)
mix test.cleanup
# Manual cleanup with verbose output
mix test.cleanup --verbose
# Dry run to see what would be cleaned
mix test.cleanup --dry-run
# Skip automatic cleanup if needed
SKIP_TEST_CLEANUP=true mix test
What Gets Cleaned
The cleanup tools will:
- Stop any Cowboy listeners from tests
- Kill registered test processes
- Free up commonly used test ports (8080-8085, 9000-9002)
- Clean up stray beam.smp processes from test runs
Manual Process Investigation
If you encounter persistent process issues:
# Check for running beam processes
ps aux | grep beam
# Check for listening ports
lsof -i :8080-8085
# Kill specific processes if needed
pkill -f "beam.*test"
Contributing
Contribution Workflow
- Fork the repository on GitHub
- Create a feature branch from
master:git checkout -b feature/your-feature-name - Make your changes following the coding standards
- Run quality checks:
make quality # Format, credo, compile checks make test # Full test suite - Commit your changes with conventional commit messages:
git commit -m "feat: add new transport option" git commit -m "fix: resolve connection timeout issue" git commit -m "docs: update configuration examples" - Push to your fork and create a pull request
Code Standards
Formatting and Style
- Always run
mix formatbefore committing - Follow Elixir community conventions
- Use descriptive variable and function names
- Add appropriate documentation to public functions
Documentation
- Add
@docto all public functions - Include examples in documentation when helpful
- Update guides when adding new features
- Ensure examples work with current codebase
Testing
- Write tests for all new functionality
- Maintain or improve test coverage
- Add appropriate test tags
- Test both success and error cases
Git Commit Messages
Follow Conventional Commits:
<type>[optional scope]: <description>
[optional body]
[optional footer(s)]Types:
feat: New featuresfix: Bug fixesdocs: Documentation changestest: Test additions or modificationsrefactor: Code refactoringperf: Performance improvementschore: Maintenance tasks
Pull Request Guidelines
Before Submitting
- [ ] All tests pass locally
- [ ] Code is formatted with
mix format - [ ] No credo warnings
- [ ] Documentation is updated if needed
- [ ] CHANGELOG.md is updated for user-facing changes
PR Description
Include:
- Clear description of the change
- Motivation and context
- Breaking changes (if any)
- Testing approach
- Related issues
Code Review Process
- Automated checks must pass (CI, formatting, tests)
- Manual review by maintainers
- Feedback incorporation and iteration
- Final approval and merge
Release Process
Version Management
ExMCP follows Semantic Versioning:
- Patch (
0.6.1): Bug fixes, documentation updates - Minor (
0.7.0): New features, non-breaking changes - Major (
1.0.0): Breaking changes
Release Checklist
Pre-Release
- [ ] All tests pass on CI
- [ ] Documentation is up to date
- [ ] CHANGELOG.md is updated
- [ ] Version is bumped in
mix.exs - [ ] Security audit passes
Release
- [ ] Tag release:
git tag v0.6.1 - [ ] Push tag:
git push origin v0.6.1 - [ ] GitHub release with changelog
- [ ] Publish to Hex:
mix hex.publish
Post-Release
- [ ] Announce release
- [ ] Update documentation sites
- [ ] Close related issues/milestones
Hotfix Process
For critical bugs in production releases:
- Create hotfix branch from release tag
- Apply minimal fix
- Update version (patch bump)
- Release immediately
- Merge back to master
Getting Help
Development Questions
- GitHub Discussions: For general development questions
- GitHub Issues: For bug reports and feature requests
- Code Review: In pull requests
Documentation
- This guide: Development setup and processes
- User Guide: Feature usage and examples
- Architecture Guide: Internal design decisions
- API Docs: Complete API reference
Community
- Elixir Forum: For general Elixir questions
- ExMCP Community: Growing community of contributors and users
Thank you for contributing to ExMCP! Your contributions help make MCP implementation in Elixir more robust and accessible to the community.