This document provides guidelines for AI agents (e.g., Claude Code agents) working on the mcpbr project. Following these guidelines ensures consistent, high-quality contributions.
- Virtual Environment Setup ← START HERE
- Pre-Commit Checklist
- Code Quality Requirements
- Testing Requirements
- Documentation Requirements
- Pull Request Guidelines
macOS uses system Python protection (PEP 668) which prevents installing packages globally. DO NOT use --break-system-packages or try to install packages system-wide. Instead, ALWAYS use a virtual environment or uv/uvx.
The project uses uv for dependency management. Use uvx for running commands and uv run for scripts:
# Run commands without installation
uvx ruff check src/ tests/
uvx ruff format src/ tests/
# Run Python scripts/tests
uv run pytest -m "not integration"
# Install project in development mode (if needed)
uv pip install -e ".[dev]"This is the preferred approach as it's already configured in the project.
If you need a traditional venv:
# Create virtual environment (one time)
python3 -m venv .venv
# Activate it (REQUIRED for every terminal session)
source .venv/bin/activate
# Install dependencies
pip install -e ".[dev]"
# Now you can use pip and run commands normally
pip install pre-commit
pytest -m "not integration"
# Deactivate when done
deactivateFor global tools like pre-commit, use pipx:
# Install pipx (one time)
brew install pipx
# Install global tools
pipx install pre-commit
pipx install pytestBefore running Python commands, verify your environment:
# Check if in a virtual environment
echo $VIRTUAL_ENV
# Should show path like: /Users/you/Projects/mcpbr/.venv
# Check Python location
which python3
# Should NOT be: /usr/bin/python3 (system Python)
# Should be: .venv/bin/python3 (venv) or managed by uvError: externally-managed-environment
# ❌ DON'T: Try to install globally
pip install something
# ✅ DO: Use uv or venv
uvx something # for CLI tools
uv run python script.py # for scripts
# OR activate venv first
source .venv/bin/activate && pip install somethingError: command not found: pytest
# ❌ DON'T: Install globally
pip install pytest
# ✅ DO: Use uv run or activate venv
uv run pytest # preferred
# OR
source .venv/bin/activate && pytestWhen starting work on this project:
# 1. Clone the repository
git clone https://github.com/greynewell/mcpbr.git
cd mcpbr
# 2. Choose your environment approach:
# Option A: Using uv (recommended)
# No setup needed! Just use `uvx` and `uv run` commands
# Option B: Using venv
python3 -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"
# 3. Install pre-commit hooks (one time)
pipx install pre-commit # if not already installed
pre-commit install
# 4. Verify setup
uv run pytest --version # Should work
uvx ruff --version # Should work- NEVER install Python packages system-wide on macOS
- ALWAYS use
uv run/uvxOR activate venv first - CHECK your environment with
echo $VIRTUAL_ENVwhen debugging - The project is configured to use
uv- preferuvxanduv runcommands
MANDATORY: Before committing code or creating a pull request, agents MUST complete ALL of the following steps:
# Check for linting issues
uvx ruff check src/ tests/
# Auto-fix fixable issues
uvx ruff check --fix src/ tests/
# Format code
uvx ruff format src/ tests/
# Verify all issues are resolved
uvx ruff check src/ tests/Expected output: All checks passed!
If any linting errors remain, they MUST be fixed manually before proceeding.
Quick one-liner:
uvx ruff check --fix src/ tests/ && uvx ruff format src/ tests/ && uvx ruff check src/ tests/# Run mypy on source code
uv run mypy src/mcpbr/Expected output: Success: no issues found
If any type errors remain, they MUST be fixed before proceeding.
# Run all non-integration tests
uv run pytest -m "not integration"
# For integration tests (if applicable)
uv run pytest -m integrationExpected result: All tests must pass with 0 failures.
MANDATORY: If your changes are user-visible, update CHANGELOG.md:
# Edit CHANGELOG.md and add entry under [Unreleased]
# Example:
# ### Fixed
# - Fixed XYZ issue (#123)
# Verify entry is properly formatted
cat CHANGELOG.md | head -30- Review all modified files
- Ensure no unintended changes were introduced
- Confirm all new code follows project conventions
- Check that imports are properly organized
- Verify CHANGELOG.md is updated (if applicable)
The project uses Ruff for linting with the following configuration:
- Line length: 100 characters (E501 is ignored)
- Target Python version: 3.11+
- Enabled rules: E (pycodestyle), F (pyflakes), I (isort), N (pep8-naming), W (warnings), B (bugbear), UP (pyupgrade), SIM (simplify), RUF (ruff-specific), C4 (comprehensions), PIE (misc), PT (pytest-style), ASYNC (async bugs), S (security/bandit), T20 (print detection)
- Type checking: mypy with Pydantic plugin, strict mode on core modules
- Unused imports - Remove all unused imports
- Import sorting - Imports must be sorted (stdlib → third-party → local)
- Undefined names - All variables and functions must be defined before use
- Line too long - While E501 is ignored, try to keep lines under 100 chars when reasonable
- Trailing whitespace - Remove trailing whitespace from all lines
- Mutable default args (B006) - Don't use
[]or{}as default arguments - Exception chaining (B904) - Use
raise X from errinsideexceptblocks - Modern Python (UP) - Use Python 3.11+ patterns (e.g.,
X | Yunions,matchstatements) - Simplifications (SIM) - Collapse nested
with/ifstatements, usecontextlib.suppress()
- Use type hints for function signatures
- Use descriptive variable names
- Follow PEP 8 naming conventions:
snake_casefor functions and variablesPascalCasefor classesUPPER_CASEfor constants
- Add docstrings for public functions and classes
- New features must have comprehensive test coverage
- Bug fixes should include regression tests
- Aim for at least 80% code coverage on new code
- Place tests in
tests/directory - Mirror source file structure (e.g.,
src/mcpbr/foo.py→tests/test_foo.py) - Use pytest fixtures for common setup
- Mark integration tests with
@pytest.mark.integration
- Test files:
test_*.py - Test classes:
Test* - Test functions:
test_* - Use descriptive names that explain what is being tested
# Run all non-integration tests
uv run pytest -m "not integration"
# Run specific test file
uv run pytest tests/test_foo.py
# Run with coverage
uv run pytest --cov=src/mcpbr --cov-report=html
# Run integration tests (requires setup)
uv run pytest -m integration- Add docstrings to all public functions, classes, and modules
- Use Google-style docstrings
- Include type hints in function signatures
Example:
def calculate_cost(tokens_in: int, tokens_out: int, model: str) -> float:
"""Calculate the cost of an API call.
Args:
tokens_in: Number of input tokens
tokens_out: Number of output tokens
model: Model identifier (e.g., 'claude-sonnet-4-5-20250929')
Returns:
Total cost in USD
Raises:
ValueError: If model is not supported
"""
...If your changes affect:
- CLI commands
- Configuration format
- Features or capabilities
- Installation or setup
Then you MUST update the README.md accordingly.
CRITICAL RULE: The CHANGELOG.md MUST be updated with EVERY user-facing change, AUTOMATICALLY, WITHOUT BEING ASKED.
Update CHANGELOG.md for ANY of these changes:
- ✅ New features or functionality
- ✅ Bug fixes
- ✅ Breaking changes
- ✅ Deprecations
- ✅ Security fixes
- ✅ Performance improvements that users will notice
- ✅ Changes to CLI commands or configuration
- ✅ Changes to output formats or APIs
- ❌ Internal refactoring with no user impact
- ❌ Test additions (unless they expose new testing capabilities)
- ❌ Documentation fixes (unless significant new documentation)
- Add entries to the
[Unreleased]section - This is where all in-progress changes go - Use the correct category:
Added- New features, capabilities, configuration optionsChanged- Changes to existing functionalityDeprecated- Features marked for removalRemoved- Removed featuresFixed- Bug fixesSecurity- Security-related changesInfrastructure- Build, release, or infrastructure changesDocumentation- Significant documentation additions (not fixes)
- Write clear, user-focused descriptions:
- Start with what changed, not how
- Include issue/PR numbers:
(#123)or(closes #123) - Use past tense
- Add sub-bullets for important details
- Format consistently:
## [Unreleased] ### Added - **Feature Name** (#123): Brief description - Additional detail about the feature - Another important detail
# 1. Make code changes
# ... edit files ...
# 2. Update CHANGELOG.md (REQUIRED - don't skip this!)
# Add entry under [Unreleased] section:
# ### Fixed
# - Fixed bug in git diff detection (#335)
# - Now detects newly created files correctly
# - Applied fallback pattern to both Docker and local modes
# 3. Commit both together
git add src/ CHANGELOG.md
git commit -m "fix: Resolve git diff detection for new files (#335)"IMPORTANT: The version in pyproject.toml on the main branch is ALWAYS the version for the NEXT release, not the current released version.
- After releasing v0.4.1, main is automatically bumped to v0.4.2
- All changes on main are being prepared for v0.4.2
- When you update CHANGELOG, add entries under
[Unreleased] - When a release is published, unreleased changes move to the new version section
The release process works like this:
-
Development Phase:
- Changes accumulate under
[Unreleased]section - Version in
pyproject.tomlreflects the upcoming release (e.g., 0.4.3)
- Changes accumulate under
-
Release Phase:
- Maintainer manually publishes release in GitHub UI (e.g., v0.4.3)
- Post-release workflow automatically bumps version to next patch (e.g., 0.4.4)
- Maintainer moves
[Unreleased]changes to## [0.4.3] - YYYY-MM-DDsection - Adds version link at bottom:
[0.4.3]: https://github.com/greynewell/mcpbr/releases/tag/v0.4.3
-
Post-Release:
[Unreleased]section is now empty, ready for next changes- Version on main is 0.4.4
- Next changes go under
[Unreleased]for eventual 0.4.4 release
CRITICAL: Always add your changes to [Unreleased]. The maintainer will move them to the versioned section during release.
Checklist for CHANGELOG:
- All user-visible changes are documented under
[Unreleased] - Entries are in the correct category (Added/Fixed/Changed/etc.)
- Descriptions are clear and user-focused
- Issue/PR numbers are included:
(#123) - Format matches existing entries
- ✅ All linting checks pass (
uvx ruff check src/ tests/) - ✅ Code is formatted (
uvx ruff format src/ tests/) - ✅ Type checking passes (
uv run mypy src/mcpbr/) - ✅ All tests pass (
uv run pytest -m "not integration") - ✅ CHANGELOG.md is updated (for user-visible changes)
- ✅ Code is documented
- ✅ README is updated (if applicable)
- ✅ Changes are committed with descriptive commit messages
Use conventional commit format:
feat:- New featuresfix:- Bug fixesdocs:- Documentation changestest:- Test additions or changesrefactor:- Code refactoringchore:- Maintenance tasks
Examples:
feat: add YAML export for resultsfix: resolve linting issuesdocs: update installation instructions
Include:
- Summary - What does this PR do?
- Changes - Detailed list of changes
- Testing - How was this tested?
- Related Issues - Link to issues using
fixes #123orcloses #123
Use GitHub keywords in PR description to auto-close issues:
fixes #123closes #123resolves #123
REQUIRED: When CodeRabbit posts review comments on your PR, you MUST acknowledge and respond to them.
If you push a commit that addresses a CodeRabbit comment:
-
Add a comment to the PR explaining what you fixed:
✅ **Resolved CodeRabbit comment about XYZ (commit abc1234)** Fixed the issue by: - Specific change 1 - Specific change 2 The corrected code now [explanation of fix].
-
Reference the specific comment if there are multiple:
✅ **Resolved: CLI override documentation issue** Fixed in commit 8185e60: - Removed incorrect `--thinking-budget` CLI override examples - Added warning that thinking_budget is YAML-only - Documented correct way to disable (omit field or set to null)
-
Be specific - Don't just say "fixed", explain what you changed
If you believe a CodeRabbit suggestion is incorrect or not applicable:
-
Explain your reasoning in a comment:
❌ **CodeRabbit suggestion not applicable** The suggestion to [X] doesn't apply here because: - Reason 1 - Reason 2 The current implementation is correct as-is.
-
Ask for maintainer input if uncertain:
❓ **Need guidance on CodeRabbit suggestion** CodeRabbit suggests [X], but I'm not sure if this is the right approach. Current approach: [Y] CodeRabbit approach: [X] @maintainer - Which approach should I take?
- Shows you're responsive to code review feedback
- Helps maintainers track which issues are resolved
- Creates a clear audit trail of what changed and why
- Prevents CodeRabbit comments from being ignored or forgotten
- Makes PR review faster (maintainers don't have to re-check everything)
# Bad: Committing without checking linting
git commit -m "feat: add new feature"
git push# Good: Check linting and types before commit
uvx ruff check --fix src/ tests/
uvx ruff format src/ tests/
uv run mypy src/mcpbr/
uv run pytest -m "not integration"
git commit -m "feat: add new feature"
git pushAlways verify tests pass locally before pushing.
# Run tests before creating PR
uv run pytest -m "not integration"
# Verify all tests pass, then pushRuff will fail if imports are not properly sorted.
# Ruff will automatically fix import sorting
uvx ruff check --fix src/ tests/Here's a complete workflow for implementing a feature:
# 1. Create a branch
git checkout -b feature/my-new-feature
# 2. Implement the feature
# ... edit files ...
# 3. Update CHANGELOG.md (REQUIRED for user-visible changes)
# Add entry under [Unreleased] section:
# ### Added
# - **New Feature**: Description of feature (#123)
# - Additional details
# 4. Check linting and format
uvx ruff check --fix src/ tests/
uvx ruff format src/ tests/
uvx ruff check src/ tests/ # Verify all fixed
# 5. Run type checking
uv run mypy src/mcpbr/
# 6. Run tests
uv run pytest -m "not integration"
# 7. Commit changes (include CHANGELOG.md)
git add src/ tests/ CHANGELOG.md
git commit -m "feat: add my new feature"
# 8. Push and create PR
git push -u origin feature/my-new-feature
gh pr create --title "feat: add my new feature" --body "Implements #123"If you encounter issues or have questions:
- Check existing issues: https://github.com/greynewell/mcpbr/issues
- Review CI/CD logs for failed checks
- Consult the main README.md for setup instructions
The project uses GitHub Actions for CI/CD. All PRs must pass:
- Lint Check -
uvx ruff check src/ tests/ - Format Check -
uvx ruff format --check src/ tests/ - Type Check -
mypy src/mcpbr/ - Build Check - Package builds successfully
- Test (Python 3.11) - All tests pass on Python 3.11
- Test (Python 3.12) - All tests pass on Python 3.12
You can view check results on any PR:
gh pr checks <PR_NUMBER>Remember: The most important rule is to run linting, formatting, type checking, and tests BEFORE committing. This ensures high code quality and prevents CI/CD failures.
Pre-commit command:
uvx ruff check --fix src/ tests/ && uvx ruff format src/ tests/ && uv run mypy src/mcpbr/ && uv run pytest -m "not integration"Happy coding! 🚀