Contributing
Thank you for your interest in contributing to NornWeave! This document provides guidelines and information for contributors.
Table of Contents
- Contributing to NornWeave
Code of Conduct
By participating in this project, you agree to abide by our Code of Conduct. Please read it before contributing.
Getting Started
Prerequisites
- Python 3.14 or higher
- uv (recommended) or pip
- Docker and Docker Compose (for local development)
- Git
Finding Issues
- Look for issues labeled
good first issuefor beginner-friendly tasks - Issues labeled
help wantedare looking for contributors - Feel free to ask questions on any issue before starting work
Development Setup
Fork and clone the repository
git clone https://github.com/YOUR_USERNAME/nornweave.git cd nornweaveInstall dependencies
# Install uv if you don't have it: https://docs.astral.sh/uv/ make install-devSet up pre-commit hooks
make setupStart the development stack
# Start PostgreSQL and Redis docker compose up -d postgres redis # Copy environment file cp .env.example .env # Run migrations make migrate # Start the development server make devVerify your setup
# Run tests make test # Run linter make lint
Making Changes
Branch Naming
Use descriptive branch names following this pattern:
feature/description- New featuresfix/description- Bug fixesdocs/description- Documentation changesrefactor/description- Code refactoringtest/description- Test additions or fixes
Commit Messages
We follow Conventional Commits. Format:
<type>(<scope>): <description>
[optional body]
[optional footer(s)]Types:
feat: New featurefix: Bug fixdocs: Documentation changesstyle: Code style changes (formatting, etc.)refactor: Code refactoringtest: Adding or updating testschore: Maintenance tasks
Examples:
feat(yggdrasil): add rate limiting middleware
fix(verdandi): handle malformed HTML in email parsing
docs(readme): update installation instructionsCoding Standards
Python Style
- Follow PEP 8 guidelines
- Use type hints for all functions and methods
- Maximum line length: 100 characters
- Use
rufffor linting and formatting
Code Quality Checks
Before submitting, ensure all checks pass:
# Run all checks
make check
# Individual checks
make lint # Linting
make format # Formatting
make typecheck # Type checkingArchitecture Guidelines
NornWeave uses a modular architecture with clear boundaries:
| Module | Responsibility |
|---|---|
core/ | Shared interfaces, config, exceptions |
models/ | Pydantic and SQLAlchemy models |
urdr/ | Storage adapters (PostgreSQL, SQLite) |
verdandi/ | Email parsing and processing |
skuld/ | Outbound sending and rate limiting |
yggdrasil/ | FastAPI routes and middleware |
adapters/ | Email provider adapters |
huginn/ | MCP read operations |
muninn/ | MCP write operations |
Key Principles:
- Depend on abstractions, not implementations
- Each module should have a clear, single responsibility
- Use dependency injection for testability
- Keep business logic separate from infrastructure
Testing
Running Tests
# All tests
make test
# Unit tests only
make test-unit
# Integration tests only
make test-integration
# With coverage
make test-covWriting Tests
- Place unit tests in
tests/unit/ - Place integration tests in
tests/integration/ - Use fixtures from
tests/conftest.py - Aim for high coverage on business logic
Test Structure:
import pytest
from nornweave.verdandi.parser import parse_email_content
class TestParseEmailContent:
"""Tests for email content parsing."""
def test_converts_html_to_markdown(self) -> None:
"""HTML should be converted to clean Markdown."""
html = "<p>Hello <strong>World</strong></p>"
result = parse_email_content(html)
assert result == "Hello **World**"
def test_removes_reply_cruft(self) -> None:
"""Reply quotes should be stripped."""
content = "Thanks!\n\nOn Jan 1, 2024, John wrote:\n> Original message"
result = parse_email_content(content)
assert "On Jan 1" not in resultDocumentation
Docstrings
Use Google-style docstrings:
def send_email(
to: list[str],
subject: str,
body: str,
*,
thread_id: str | None = None,
) -> str:
"""Send an email through the configured provider.
Args:
to: List of recipient email addresses.
subject: Email subject line.
body: Email body in Markdown format.
thread_id: Optional thread ID for replies.
Returns:
The provider's message ID.
Raises:
EmailProviderError: If sending fails.
ValidationError: If recipients are invalid.
"""Updating Documentation
Documentation is in the docs/ directory using MkDocs:
# Serve locally
make docs
# Build
make docs-buildSubmitting Changes
Pull Request Process
- Create a PR with a clear title and description
- Link related issues using keywords (e.g., “Fixes #123”)
- Ensure CI passes - all checks must be green
- Request review from maintainers
- Address feedback and update as needed
- Squash and merge once approved
PR Description Template
## Summary
Brief description of changes.
## Changes
- Change 1
- Change 2
## Testing
How were these changes tested?
## Checklist
- [ ] Tests added/updated
- [ ] Documentation updated
- [ ] CHANGELOG updated (for user-facing changes)Release Process
Releases are managed by maintainers following Semantic Versioning:
- MAJOR: Breaking API changes
- MINOR: New features, backward compatible
- PATCH: Bug fixes, backward compatible
Main NornWeave Release
The main NornWeave server is published to both GitHub Releases and PyPI.
Prerequisites (one-time setup):
- PyPI project
nornweaveexists - Trusted publisher configured on PyPI for this repo (OIDC)
- GitHub environment
pypi-publishconfigured
Release steps:
# 1. Update version in pyproject.toml
# Edit: version = "X.Y.Z"
# 2. Update CHANGELOG.md
# - Move items from [Unreleased] to new version section
# - Add release date: ## [X.Y.Z] - YYYY-MM-DD
# 3. Commit the changes
git add pyproject.toml CHANGELOG.md
git commit -m "chore: release vX.Y.Z"
# 4. Create annotated tag (must match pyproject.toml version)
git tag -a vX.Y.Z -m "Release vX.Y.Z"
# 5. Push commit and tag (triggers PyPI publish)
git push origin main --tagsThe GitHub Actions workflow (.github/workflows/server-pypi-publish.yml) will:
- Build source distribution and wheel using
hatch build - Publish to PyPI using trusted publishing (OIDC, no token needed)
The GitHub release is created manually:
- Go to Releases
- Click “Draft a new release”
- Select the tag
vX.Y.Z - Use “Generate release notes” or copy from CHANGELOG.md
- Publish the release
Important: The vX.Y.Z tag must match the version in pyproject.toml. For example, tag v0.1.1 requires version = "0.1.1" in pyproject.toml.
n8n Node Release (@nornweave/n8n-nodes-nornweave)
The n8n community node is published to npm with provenance attestation.
Release steps:
cd packages/n8n-nodes-nornweave
# 1. Bump version
npm version patch # or minor/major
# 2. Commit the version bump
git add package.json package-lock.json
git commit -m "chore(n8n): release vX.Y.Z"
# 3. Create and push tag (triggers CI publish)
git tag n8n-vX.Y.Z
git push origin main --tagsThe GitHub Actions workflow (.github/workflows/n8n-node.yml) will:
- Build and test the package
- Publish to npm with provenance (OIDC, no token needed)
- Create a GitHub release
Python Client Release (nornweave-client)
The Python client is published to PyPI using trusted publishing (OIDC).
Prerequisites (one-time setup):
- PyPI project
nornweave-clientexists - Trusted publisher configured on PyPI for this repo
- GitHub environment
pypi-publishconfigured
Release steps:
cd clients/python
# 1. Update version in pyproject.toml
# Edit: version = "X.Y.Z"
# 2. Update CHANGELOG if needed
# 3. Commit the version bump
git add pyproject.toml
git commit -m "chore(python-client): release vX.Y.Z"
# 4. Create and push tag (triggers CI publish)
git tag python-vX.Y.Z
git push origin main --tagsThe GitHub Actions workflow (.github/workflows/client-pypi-publish.yml) will:
- Run tests and linting
- Build source distribution and wheel
- Publish to PyPI with OIDC (no token needed)
First-time publish (local):
For the initial publish before OIDC is configured:
cd clients/python
# Build the package
uv build
# or: python -m build
# Upload to PyPI (requires API token)
uv publish
# or: twine upload dist/*Then configure trusted publishing on PyPI by adding the GitHub repository as a trusted publisher.
Version Alignment
The packages are versioned independently:
| Package | Version Location | Tag Format | Registry |
|---|---|---|---|
| NornWeave Server | pyproject.toml | vX.Y.Z | PyPI (nornweave) |
| n8n Node | packages/n8n-nodes-nornweave/package.json | n8n-vX.Y.Z | npm |
| Python Client | clients/python/pyproject.toml | python-vX.Y.Z | PyPI (nornweave-client) |
Important:
- The
vX.Y.Ztag on the monorepo matches the mainnornweavePyPI package version - When making breaking API changes to the server, coordinate releases across all packages to ensure compatibility
MCP Registry Submission
NornWeave is listed on several MCP registries to help users discover the MCP server. When releasing a new version, update the registry listings:
Registry Metadata Files
The repository includes metadata files for each registry:
| File | Registry | Documentation |
|---|---|---|
smithery.yaml | Smithery.ai | Docs |
mcp-get.json | mcp-get.com | Docs |
glama.json | Glama.ai | Docs |
Updating Registry Listings
Update metadata files - When adding new tools, resources, or changing capabilities, update the corresponding metadata files in the repository root.
Version bump - Update the
versionfield in all metadata files to match the new release version.Submit updates - Most registries automatically index from GitHub. For manual submission:
- Smithery.ai: Submit via smithery.ai/submit
- mcp-get.com: Open a PR to the mcp-get/packages repository
- Glama.ai: Submit via glama.ai/mcp/submit
Verify listings - After release, verify the server appears correctly on each registry.
Questions?
- Open a GitHub Discussion
- Check existing issues
- Read the documentation
Thank you for contributing to NornWeave!