Skip to content

Contributing

Setup

bash
git clone https://github.com/mcp-hangar/mcp-hangar.git
cd mcp-hangar

# Python core development
cd packages/core
pip install -e ".[dev]"

# Or use root Makefile
cd ../..
make setup

Monorepo Structure

MCP Hangar is a monorepo with multiple packages:

mcp-hangar/
├── packages/
│   ├── core/                # Python package (PyPI: mcp-hangar)
│   │   ├── mcp_hangar/      # Main Python code
│   │   ├── tests/           # Python tests
│   │   └── pyproject.toml   # Python package config
│   ├── operator/            # Kubernetes operator (Go)
│   │   ├── api/             # CRD definitions
│   │   ├── cmd/             # Main entrypoints
│   │   ├── internal/        # Controller logic
│   │   └── go.mod           # Go module config
│   └── helm-charts/         # Helm charts
│       ├── mcp-hangar/      # Core Helm chart
│       └── mcp-hangar-operator/  # Operator Helm chart
├── docs/                    # MkDocs documentation
├── examples/                # Quick starts & demos
├── monitoring/              # Grafana, Prometheus configs
└── Makefile                 # Root orchestration

Python Core Structure

packages/core/mcp_hangar/
├── domain/           # DDD domain layer
│   ├── model/        # Aggregates, entities
│   ├── services/     # Domain services
│   ├── events.py     # Domain events
│   └── exceptions.py
├── application/      # Application layer
│   ├── commands/     # CQRS commands
│   ├── queries/      # CQRS queries
│   └── sagas/
├── infrastructure/   # Infrastructure adapters
├── server/           # MCP server module
│   ├── __init__.py   # Main entry point
│   ├── config.py     # Configuration loading
│   ├── state.py      # Global state management
│   └── tools/        # MCP tool implementations
├── observability/    # Metrics, tracing, health
├── stdio_client.py   # JSON-RPC client
└── gc.py             # Background workers

Code Style

bash
cd packages/core
ruff check mcp_hangar tests --fix
ruff format mcp_hangar tests
mypy mcp_hangar

Conventions

ItemStyle
ClassesPascalCase
Functionssnake_case
ConstantsUPPER_SNAKE_CASE
EventsPascalCase + past tense (ProviderStarted)

Type Hints

Required for all new code. Use Python 3.11+ built-in generics:

python
def invoke_tool(
    self,
    tool_name: str,
    arguments: dict[str, Any],
    timeout: float = 30.0,
) -> dict[str, Any]:
    ...

Testing

bash
cd packages/core
pytest -v -m "not slow"
pytest --cov=mcp_hangar --cov-report=html

# Or from root
make test

Target: >80% coverage on new code.

Writing Tests

python
def test_tool_invocation():
    # Arrange
    provider = Provider(provider_id="test", mode="subprocess", command=[...])

    # Act
    result = provider.invoke_tool("add", {"a": 1, "b": 2})

    # Assert
    assert result["result"] == 3

Pull Requests

  1. Create feature branch

  2. Make changes following style guidelines

  3. Add tests

  4. Run checks:

    bash
    cd packages/core
    pytest -v -m "not slow"
    pre-commit run --all-files
  5. Update docs if needed

PR Template

markdown
## Description
Brief description.

## Type
- [ ] Bug fix
- [ ] New feature
- [ ] Breaking change

## Testing
- [ ] Unit tests added
- [ ] All tests pass

Architecture Guidelines

Value Objects:

python
provider_id = ProviderId("my-provider")  # Validated

Events:

python
provider.ensure_ready()
for event in provider.collect_events():
    event_bus.publish(event)

Exceptions:

python
# Basic usage
raise ProviderStartError(
    provider_id="my-provider",
    reason="Connection refused"
)

# With diagnostics (preferred)
raise ProviderStartError(
    provider_id="my-provider",
    reason="MCP initialization failed: process crashed",
    stderr="ModuleNotFoundError: No module named 'requests'",
    exit_code=1,
    suggestion="Install missing Python dependencies."
)

# Get user-friendly message
try:
    provider.ensure_ready()
except ProviderStartError as e:
    print(e.get_user_message())

Logging:

python
logger.info("provider_started: %s, mode=%s", provider_id, mode)

Releasing

Release Process Overview

MCP Hangar uses automated CI/CD for releases. The process ensures quality through:

  1. Version Validation — Tag must match pyproject.toml version
  2. Full Test Suite — All tests across Python 3.11-3.14
  3. Security Scanning — Dependency audit and container scanning
  4. Artifact Publishing — PyPI package and Docker images

Creating a Release

Use the GitHub Actions workflow:

  1. Go to ActionsVersion Bump
  2. Click Run workflow
  3. Select bump type: patch, minor, or major
  4. Optionally select pre-release suffix (alpha.1, beta.1, rc.1)
  5. Run (or use dry run to preview)

The workflow will:

  • Update version in pyproject.toml
  • Update CHANGELOG.md with release date
  • Create and push the version tag
  • Trigger the release pipeline automatically

Option 2: Manual

bash
# 1. Update version in packages/core/pyproject.toml
cd packages/core
sed -i '' 's/version = ".*"/version = "1.2.0"/' pyproject.toml

# 2. Update CHANGELOG.md - move Unreleased items to new version section

# 3. Commit changes
git add pyproject.toml CHANGELOG.md
git commit -m "chore: bump version to 1.2.0"

# 4. Create annotated tag
git tag -a v1.2.0 -m "Release v1.2.0"

# 5. Push
git push origin main
git push origin v1.2.0

Pre-release Versions

Pre-releases are automatically published to TestPyPI:

bash
# Tag patterns for pre-releases
v1.0.0-alpha.1  # Alpha release
v1.0.0-beta.1   # Beta release
v1.0.0-rc.1     # Release candidate

Install pre-release:

bash
pip install --index-url https://test.pypi.org/simple/ mcp-hangar==1.0.0rc1

Release Checklist

Before releasing, ensure:

  • [ ] All tests pass locally: cd packages/core && pytest -v
  • [ ] Linting passes: pre-commit run --all-files
  • [ ] CHANGELOG.md is updated with all notable changes
  • [ ] Documentation is updated for new features
  • [ ] Breaking changes are clearly documented
  • [ ] Version follows Semantic Versioning

Versioning Guidelines

We follow Semantic Versioning (SemVer):

Change TypeVersion BumpExample
Bug fixes, patchesPATCH1.0.0 → 1.0.1
New features (backward-compatible)MINOR1.0.1 → 1.1.0
Breaking changesMAJOR1.1.0 → 2.0.0

Release Artifacts

Each release produces:

ArtifactLocationTags
Python PackagePyPIVersion number
Docker ImageGHCRlatest, X.Y.Z, X.Y, X
GitHub ReleaseRepository ReleasesChangelog, install instructions

Hotfix Process

For urgent fixes on released versions:

bash
# 1. Create hotfix branch from tag
git checkout -b hotfix/1.0.1 v1.0.0

# 2. Apply fix, add tests

# 3. Update version and changelog
# 4. Tag and push

git tag -a v1.0.1 -m "Hotfix: description"
git push origin v1.0.1

# 5. Cherry-pick to main if applicable
git checkout main
git cherry-pick <commit-hash>

License

MIT

Code of Conduct

Please read our Code of Conduct before contributing.

First Contribution?

Look for issues labeled good first issue.

Questions? Open a Discussion.

Released under the MIT License.