Thank you for your interest in contributing to the Things 3 MCP Server! This guide will help you get started with contributing to our open-source project.
We welcome all types of contributions:
- π Bug Reports - Help us identify and fix issues
- π‘ Feature Requests - Suggest new functionality
- π Documentation - Improve guides, examples, and API docs
- π§ͺ Tests - Add test coverage for better reliability
- β¨ Code Contributions - Implement features and fix bugs
- π¨ Examples - Create usage examples and tutorials
- π¬ Community Support - Help others in discussions
# Fork the repository on GitHub, then clone your fork
git clone https://github.com/YOUR_USERNAME/things-mcp-server.git
cd things-mcp-server
# Add upstream remote
git remote add upstream https://github.com/yourusername/things-mcp-server.git# Create virtual environment
python -m venv venv
source venv/bin/activate # On macOS/Linux
# Install development dependencies
pip install -e ".[dev]"
# Install pre-commit hooks
pre-commit install# Always create a new branch for your work
git checkout -b feature/your-feature-nameFollow our coding standards and add tests for your changes.
# Run the full test suite
pytest
# Run with coverage
pytest --cov=src/things_mcp --cov-report=html
# Run linting
flake8 src/ tests/
black --check src/ tests/
isort --check src/ tests/
mypy src/# Push your branch
git push origin feature/your-feature-nameThen open a Pull Request on GitHub.
- macOS 12.0 or later
- Python 3.8+
- Things 3 installed
- Git
- IDE with Python support (VS Code, PyCharm, etc.)
# 1. Clone and setup
git clone https://github.com/YOUR_USERNAME/things-mcp-server.git
cd things-mcp-server
# 2. Create virtual environment
python -m venv venv
source venv/bin/activate
# 3. Install all dependencies
pip install -e ".[dev,test,docs]"
# 4. Setup pre-commit hooks
pre-commit install
# 5. Copy configuration files
cp .env.example .env
# 6. Run tests to verify setup
pytestInstall recommended extensions:
- Python
- Pylance
- Black Formatter
- isort
- Python Test Explorer
Add to your .vscode/settings.json:
{
"python.defaultInterpreterPath": "./venv/bin/python",
"python.linting.enabled": true,
"python.linting.flake8Enabled": true,
"python.formatting.provider": "black",
"python.sortImports.args": ["--profile", "black"],
"editor.formatOnSave": true,
"python.testing.pytestEnabled": true,
"python.testing.pytestArgs": ["tests/"]
}- Open project in PyCharm
- Configure Python interpreter to use
./venv/bin/python - Enable Black as code formatter
- Configure pytest as test runner
- Enable type checking with mypy
tests/
βββ unit/ # Unit tests
β βββ test_tools.py
β βββ test_models.py
β βββ test_applescript_manager.py
βββ integration/ # Integration tests
β βββ test_server_lifecycle.py
β βββ test_error_handling.py
β βββ test_cli_interface.py
βββ fixtures/ # Test data and fixtures
β βββ sample_todos.json
β βββ mock_responses.py
βββ conftest.py # Pytest configuration
# tests/unit/test_example.py
import pytest
from unittest.mock import Mock, patch
from things_mcp.tools import ThingsTools
class TestThingsTools:
def test_add_todo_basic(self):
"""Test basic todo creation."""
applescript_manager = Mock()
tools = ThingsTools(applescript_manager)
# Test implementation
result = tools.add_todo(title="Test Todo")
# Assertions
assert result["success"] is True
applescript_manager.execute_script.assert_called_once()
@pytest.mark.asyncio
async def test_add_todo_async(self):
"""Test async todo creation."""
# Async test implementation
pass# tests/integration/test_server.py
import pytest
from things_mcp.server import ThingsMCPServer
@pytest.mark.integration
class TestServerIntegration:
@pytest.fixture
def server(self):
return ThingsMCPServer()
async def test_health_check_integration(self, server):
"""Test actual health check against Things 3."""
health = await server.health_check()
assert health["server_status"] == "healthy"Use pytest markers to categorize tests:
@pytest.mark.unit # Fast unit tests
@pytest.mark.integration # Slower integration tests
@pytest.mark.applescript # Tests requiring Things 3
@pytest.mark.slow # Tests that take >5 seconds
@pytest.mark.skip_ci # Skip in CI environment# Run all tests
pytest
# Run specific categories
pytest -m "unit" # Only unit tests
pytest -m "not slow" # Skip slow tests
pytest -m "integration" # Only integration tests
# Run with coverage
pytest --cov=src/things_mcp --cov-report=html
# Run specific test file
pytest tests/unit/test_tools.py
# Run specific test
pytest tests/unit/test_tools.py::TestThingsTools::test_add_todo_basic
# Run with verbose output
pytest -v
# Run tests in parallel (faster)
pytest -n autoWe use these tools to maintain consistent code style:
- Black: Code formatting
- isort: Import sorting
- Flake8: Linting
- mypy: Type checking
# Format code
black src/ tests/
isort src/ tests/
# Check formatting
black --check src/ tests/
isort --check src/ tests/
# Lint code
flake8 src/ tests/
# Type check
mypy src/def add_todo(
self,
title: str,
notes: Optional[str] = None,
tags: Optional[List[str]] = None,
when: Optional[str] = None,
deadline: Optional[str] = None
) -> Dict[str, Any]:
"""Add a new todo to Things 3.
Args:
title: The todo title (required)
notes: Optional notes for the todo
tags: List of tag names to apply
when: When to schedule (today, tomorrow, YYYY-MM-DD, etc.)
deadline: Deadline date in YYYY-MM-DD format
Returns:
Dict containing creation result with success status and todo data
Raises:
AppleScriptError: If Things 3 is not accessible
ValidationError: If parameters are invalid
Example:
>>> result = await tools.add_todo(
... title="Review proposal",
... notes="Check budget section",
... tags=["work", "urgent"],
... when="tomorrow",
... deadline="2024-01-30"
... )
>>> assert result["success"] is True
"""Use type hints throughout:
from typing import Any, Dict, List, Optional, Union
from datetime import datetime
class ThingsTools:
def __init__(self, applescript_manager: AppleScriptManager) -> None:
self.applescript = applescript_manager
async def get_todos(
self,
project_uuid: Optional[str] = None
) -> List[Dict[str, Any]]:
# Implementation
passimport logging
from typing import Any, Dict
logger = logging.getLogger(__name__)
async def risky_operation() -> Dict[str, Any]:
"""Example of proper error handling."""
try:
result = await some_operation()
return {"success": True, "data": result}
except SpecificError as e:
logger.error(f"Specific error occurred: {e}")
return {
"success": False,
"error": f"Operation failed: {e}",
"error_code": "SPECIFIC_ERROR"
}
except Exception as e:
logger.error(f"Unexpected error: {e}", exc_info=True)
return {
"success": False,
"error": "An unexpected error occurred",
"error_code": "UNKNOWN_ERROR"
}# Standard library imports first
import asyncio
import logging
from datetime import datetime
from typing import Any, Dict, List, Optional
# Third-party imports
import pytest
from fastmcp import FastMCP
# Local imports
from .applescript_manager import AppleScriptManager
from .models import Todo, ProjectWe use Google-style docstrings:
def example_function(param1: str, param2: int = 0) -> bool:
"""Brief description of what the function does.
Longer description if needed. This can span multiple lines
and include examples, implementation notes, etc.
Args:
param1: Description of first parameter
param2: Description of second parameter (default: 0)
Returns:
Description of return value
Raises:
ValueError: When param1 is empty
TypeError: When param2 is not an integer
Example:
>>> result = example_function("test", 5)
>>> assert result is True
Note:
Any important implementation details or caveats
"""- Keep language clear and accessible
- Use examples for all concepts
- Include troubleshooting sections
- Update documentation when changing APIs
- Test all code examples
- Check existing issues
- Update to latest version
- Test with minimal reproduction case
Use our issue template or include:
## Bug Description
Brief description of the issue
## Steps to Reproduce
1. First step
2. Second step
3. Third step
## Expected Behavior
What should happen
## Actual Behavior
What actually happens
## Environment
- OS: macOS 13.2
- Python: 3.11.1
- Things 3: 3.19.1
- Server Version: 1.0.0
## Additional Context
Any other relevant information, error messages, logs- Check existing feature requests
- Consider if it fits project scope
- Think about implementation approach
## Feature Description
Clear description of the proposed feature
## Use Case
Why is this needed? What problem does it solve?
## Proposed Solution
How should this feature work?
## Alternatives Considered
What other approaches did you consider?
## Additional Context
Any other relevant information, mockups, examples- Tests pass locally
- Code follows style guidelines
- Documentation is updated
- CHANGELOG.md is updated
- Commits are well-formatted
## Description
Brief description of changes
## Type of Change
- [ ] Bug fix (non-breaking change)
- [ ] New feature (non-breaking change)
- [ ] Breaking change
- [ ] Documentation update
## Testing
- [ ] Unit tests added/updated
- [ ] Integration tests added/updated
- [ ] Manual testing completed
## Checklist
- [ ] Code follows project style guidelines
- [ ] Self-review completed
- [ ] Documentation updated
- [ ] Tests added for new functionality- Automated Checks: CI must pass
- Code Review: At least one maintainer review
- Testing: Feature testing by reviewers
- Documentation: Docs review if applicable
- Merge: Squash and merge when approved
Contributors are recognized in:
- CHANGELOG.md for each release
- README.md contributors section
- GitHub repository insights
- Release notes for major contributions
- GitHub Issues: Bug reports and feature requests
- GitHub Discussions: Questions and general discussion
- Discord: Real-time chat (link in README)
- Email: maintainer@yourdomain.com
For development help:
- Check existing documentation
- Search closed issues/PRs
- Ask in GitHub Discussions
- Join our Discord community
This project follows the Contributor Covenant Code of Conduct. By participating, you agree to uphold this code.
- Be respectful and inclusive
- Welcome newcomers
- Focus on constructive feedback
- Respect different viewpoints
- Report unacceptable behavior
- Start with "good first issue" labels
- Documentation improvements
- Test additions
- Bug reports with reproduction steps
- Feature implementations
- Code reviews
- Issue triage
- Community support
- Architecture decisions
- Release management
- Mentoring new contributors
- Project direction
For major contributions, ensure:
- Design Discussion: For large features, discuss approach first
- Tests: Comprehensive test coverage
- Documentation: All public APIs documented
- Performance: No significant performance regressions
- Backward Compatibility: Changes don't break existing users
- Security: No security vulnerabilities introduced
- Accessibility: Features work for all users
Thank you for contributing to Things 3 MCP Server! π