This document provides a complete step-by-step workflow for developing amplifier-foundation modules from initial design through publishing and maintenance.
- Define Module Responsibility
- Write Contract (README First)
- Create Module Structure
- Implement Protocol
- Local Testing Strategies
- Testing Workflow
- Publishing to GitHub
- Versioning and Tagging
- User Consumption Patterns
- Maintenance and Updates
Before writing any code, clearly define what your module does.
Ask yourself: "Can I describe this module in one sentence?"
Good examples (single responsibility):
- "Convert text to uppercase"
- "Log agent events to a file"
- "Search files for regex patterns"
- "Store conversation history in SQLite"
Bad examples (too broad):
- "Handle all file operations" → Split into: read, write, search tools
- "Manage everything about conversations" → Split into: context, search, export
- "Do AI stuff" → Not specific enough
Before proceeding, answer these questions:
- Can I describe this module in one sentence?
- Does it do exactly one thing well?
- Would splitting it make it more useful?
- Can I test it independently?
- Will users understand what it does from the name?
Follow the convention: amplifier-module-{type}-{name}
Type prefix:
tool-for toolshook-for hooksprovider-for providerscontext-for contextsloop-for orchestrators
Name should be:
- Descriptive (what it does)
- Kebab-case
- No redundant prefixes (not
tool-file-tool)
Examples:
amplifier-module-tool-filesystemamplifier-module-hook-loggingamplifier-module-provider-anthropicamplifier-module-context-memory
The README.md is your public contract. Write it before implementing code.
# amplifier-module-{type}-{name}
> One-sentence description of what this module does
## Installation
\`\`\`bash
uv pip install git+https://github.com/yourusername/amplifier-module-{type}-{name}.git
\`\`\`
## Features
- Feature 1
- Feature 2
- Feature 3
## Usage
### Basic Usage
\`\`\`yaml
# In your profile.md or bundle.md
{type}s:
- git+https://github.com/yourusername/amplifier-module-{type}-{name}.git@v1.0.0
\`\`\`
### Configuration
\`\`\`yaml
{type}_config:
option1: value1
option2: value2
\`\`\`
## API
### mount(coordinator, config) -> dict
**Returns**: Dict with the following functions/objects:
#### function_name(arg1: type, arg2: type) -> return_type
Description of what this function does.
**Arguments**:
- `arg1`: Description
- `arg2`: Description
**Returns**: Description of return value
**Raises**:
- `ExceptionType`: When this happens
**Example**:
\`\`\`python
tools = await mount(coordinator, config={})
result = await tools["function_name"](arg1="value", arg2=42)
\`\`\`
## Configuration Reference
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| option1 | str | "default" | What this does |
| option2 | int | 100 | What this does |
## Examples
### Example 1: Common Use Case
\`\`\`python
# Code example
\`\`\`
### Example 2: Advanced Usage
\`\`\`python
# Code example
\`\`\`
## Testing
\`\`\`bash
uv pip install pytest pytest-asyncio
uv run pytest tests/
\`\`\`
## License
MIT- Be specific: Exact type signatures, not vague descriptions
- Show examples: Real code that users can copy
- Document all options: Every config key with type and default
- Explain errors: What exceptions can be raised and why
- Include examples: At least 2-3 usage examples
Create the directory structure and boilerplate files.
# Create module directory
mkdir amplifier-module-{type}-{name}
cd amplifier-module-{type}-{name}
# Create package directory (underscores)
mkdir amplifier_module_{type}_{name}
# Create tests directory
mkdir tests
# Create GitHub Actions directory
mkdir -p .github/workflows[project]
name = "amplifier-module-{type}-{name}"
version = "0.1.0"
description = "One sentence description"
readme = "README.md"
requires-python = ">=3.11"
license = { text = "MIT" }
authors = [
{ name = "Your Name", email = "your.email@example.com" }
]
dependencies = [
"amplifier-foundation>=0.1.0",
# Add other dependencies here
]
[project.optional-dependencies]
dev = [
"pytest>=7.4.0",
"pytest-asyncio>=0.21.0",
"pytest-cov>=4.1.0",
]
# Entry point registration
[project.entry-points."amplifier.{type}s"] # Note: plural
{name} = "amplifier_module_{type}_{name}:mount"
# For tool modules: also register schema
[project.entry-points."amplifier.tool_schemas"] # Only for tools
{name} = "amplifier_module_{type}_{name}:get_schema"
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[tool.pytest.ini_options]
testpaths = ["tests"]
python_files = "test_*.py"
python_functions = "test_*"
asyncio_mode = "auto"
[tool.coverage.run]
source = ["amplifier_module_{type}_{name}"]
omit = ["tests/*", "*/__pycache__/*"]
[tool.coverage.report]
exclude_lines = [
"pragma: no cover",
"def __repr__",
"raise AssertionError",
"raise NotImplementedError",
"if __name__ == .__main__.:",
"if TYPE_CHECKING:",
]# amplifier_module_{type}_{name}/__init__.py
"""
amplifier-module-{type}-{name}
One sentence description.
"""
from typing import Any
__version__ = "0.1.0"
async def mount(coordinator: Any, config: dict) -> dict[str, Any]:
"""Mount the module.
Args:
coordinator: The amplifier coordinator
config: Configuration dictionary
Returns:
Dict with module functions
"""
# TODO: Implement
pass
# For tool modules, also implement:
def get_schema() -> dict:
"""Return JSON schema for tool functions.
Returns:
Dict mapping function names to schemas
"""
# TODO: Implement
passtouch amplifier_module_{type}_{name}/py.typed# tests/conftest.py
import pytest
@pytest.fixture
async def mounted_module():
"""Fixture that mounts the module."""
from amplifier_module_{type}_{name} import mount
return await mount(coordinator=None, config={})
@pytest.fixture
def sample_config():
"""Sample configuration for tests."""
return {
"option1": "value1",
"option2": 42
}# tests/test_unit.py
import pytest
@pytest.mark.asyncio
async def test_mount_succeeds():
"""Test that module mounts successfully."""
from amplifier_module_{type}_{name} import mount
module = await mount(coordinator=None, config={})
assert isinstance(module, dict)
# Add more tests here# Python
__pycache__/
*.py[cod]
*$py.class
*.so
.Python
env/
venv/
.venv/
ENV/
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg
# Testing
.pytest_cache/
.coverage
htmlcov/
.tox/
# IDEs
.vscode/
.idea/
*.swp
*.swo
*~
# OS
.DS_Store
Thumbs.db
# .github/workflows/test.yml
name: Tests
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.11", "3.12"]
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Install uv
uses: astral-sh/setup-uv@v1
- name: Install dependencies
run: |
uv pip install -e ".[dev]"
- name: Run tests with coverage
run: |
uv run pytest --cov --cov-report=xml --cov-report=term
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v3
with:
file: ./coverage.xmlNow implement the mount() function and any required methods.
# amplifier_module_{type}_{name}/__init__.py
from typing import Any
from ._internal import Implementation # Private implementation
async def mount(coordinator: Any, config: dict) -> dict[str, Any]:
"""Mount the module."""
# Validate config
_validate_config(config)
# Create implementation instance
impl = Implementation(config)
# Initialize resources
await impl.initialize()
# Return public interface
return {
"function1": impl.function1,
"function2": impl.function2,
}
def _validate_config(config: dict):
"""Validate configuration (private helper)."""
required = ["option1"]
for key in required:
if key not in config:
raise ValueError(f"Missing required config: {key}")# amplifier_module_{type}_{name}/_internal.py
"""Private implementation details."""
class Implementation:
"""Private implementation class."""
def __init__(self, config: dict):
self._config = config
self._cache = {}
async def initialize(self):
"""Initialize resources."""
# Setup connections, load data, etc.
pass
async def function1(self, arg: str) -> str:
"""Public function."""
return self._process(arg)
def _process(self, arg: str) -> str:
"""Private helper method."""
return arg.upper()Test your module locally before publishing.
# Set environment variable to your local module
export AMPLIFIER_MODULE_TOOL_MYMODULE=$(pwd)
# Test with a simple profile
cat > test_profile.md << 'EOF'
---
tools:
- mymodule
---
# Test Profile
EOF
# Run test
python -c "
from amplifier_foundation import load_bundle, create_session
import asyncio
async def test():
bundle = await load_bundle('./test_profile.md')
session = await create_session(bundle)
print('Module loaded successfully!')
asyncio.run(test())
"Create a workspace configuration:
# workspace.yaml
name: module-dev-workspace
python: "3.11"
sources:
- file://./amplifier-module-tool-mymodule # Your local module
- git+https://github.com/microsoft/amplifier-foundation.git # CoreInstall workspace:
uv pip install -e ./amplifier-module-tool-mymodule
uv pip install amplifier-foundationCreate a dev profile that uses your local module:
# dev_profile.md
---
tools:
- file://./amplifier-module-tool-mymodule
provider:
name: anthropic
config:
model: claude-3-5-sonnet
---
# Development Profile
This profile uses the local development version of the module.Test:
python -c "
from amplifier_foundation import load_bundle
import asyncio
asyncio.run(load_bundle('./dev_profile.md'))
"Follow the test pyramid: 60% unit, 30% integration, 10% e2e.
uv run pytest tests/test_unit.py -vuv run pytest tests/test_integration.py -vuv run pytest --cov=amplifier_module_{type}_{name} --cov-report=html
open htmlcov/index.htmluv run pytest --cov --cov-report=term
# Should show:
# Coverage: 85% (target)
# Critical paths: 100%git init
git add .
git commit -m "feat: initial module implementation
- Implement mount() function
- Add tests with 85% coverage
- Document API in README
🤖 Generated with Claude Code
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>"gh repo create amplifier-module-{type}-{name} --public
git push -u origin maingh repo edit --description "One sentence description of module"
gh repo edit --add-topic amplifier-module
gh repo edit --add-topic amplifier-{type}Follow Semantic Versioning:
- Major (1.0.0): Breaking changes
- Minor (0.1.0): New features, backward compatible
- Patch (0.0.1): Bug fixes
# Update version in pyproject.toml
# version = "0.1.0"
git add pyproject.toml
git commit -m "chore: bump version to 0.1.0"
git tag v0.1.0
git push origin main --tagsgh release create v0.1.0 \
--title "Initial Release v0.1.0" \
--notes "First stable release
Features:
- Feature 1
- Feature 2
- Feature 3
📦 Install:
\`\`\`yaml
tools:
- git+https://github.com/yourusername/amplifier-module-{type}-{name}.git@v0.1.0
\`\`\`"Users reference your module by git URL:
# profile.md
---
tools:
- git+https://github.com/yourusername/amplifier-module-tool-mymodule.git@v0.1.0
---During development, users can use local paths:
# profile.md
---
tools:
- file://./amplifier-module-tool-mymodule
---Users can compose your module into bundles:
# bundle.md
---
tools:
- git+https://github.com/yourusername/amplifier-module-tool-mymodule.git@v0.1.0
- git+https://github.com/otheruser/amplifier-module-tool-another.git@v1.2.0
----
Create feature branch:
git checkout -b feat/new-feature
-
Make changes and update tests
-
Run tests:
uv run pytest --cov
-
Update README if API changed
-
Commit changes:
git add . git commit -m "feat: add new feature"
-
Push and create PR:
git push origin feat/new-feature gh pr create
-
After merge, tag new version:
git checkout main git pull # Update version in pyproject.toml git commit -m "chore: bump version to 0.2.0" git tag v0.2.0 git push origin main --tags gh release create v0.2.0
When making breaking changes:
- Bump major version (e.g., 0.5.0 → 1.0.0)
- Document migration in CHANGELOG.md
- Provide migration examples
- Consider deprecation period
Before considering a module "done":
- Responsibility is single and clear
- README documents complete API
- pyproject.toml has correct entry points
- Tests achieve 85% coverage
- Tests follow 60/30/10 pyramid
- Local testing succeeds
- Examples in README work
- Code passes linting
- Git repository created
- Tagged with semantic version
- GitHub release published
- Repository has description and topics
- Read TESTING_GUIDE.md for comprehensive testing strategies
- Study EXAMPLES.md for complete working modules
- Review API_PATTERNS.md for common implementation patterns
- Check CONTRIBUTING.md for community guidelines