Contributing¶
Thanks for your interest in contributing to YNAB MCP! This guide covers everything you need to get started.
Development setup¶
1. Clone the repository:
2. Install dependencies (requires uv):
3. Install pre-commit hooks:
Pre-commit runs Ruff (lint + format) and Prettier (JSON, YAML, Markdown) on every commit.
Running tests¶
Tests use pytest with pytest-socket to disable network access by default -- no real YNAB API calls during testing.
# Run all tests
uv run pytest
# Run a single test file
uv run pytest tests/test_client.py
# Run tests matching a keyword
uv run pytest -k "rate_limit"
# Run with verbose output
uv run pytest -v
Testing conventions¶
- pytest-mock over
unittest.mock-- use themockerfixture for patching. It auto-cleans after each test and is more pytest-idiomatic. - freezegun for wall-clock time -- use
@freeze_timefor tests involvingdatetime.now()ordate.today(). Not applicable totime.monotonic()(usemocker.patchfor monotonic). - Hypothesis for property-based tests -- use
@givenwith strategies for functions with well-defined input/output contracts (converters, validators, parsers). Complements example-based tests, doesn't replace them.
Code style¶
Ruff handles both linting and formatting. The project uses an aggressive rule set:
With specific ignores listed in pyproject.toml. In practice this means:
- Type annotations on everything -- public functions, methods, parameters, return types
- Google-style docstrings on all public functions and classes
- No magic numbers -- use named constants or
http.HTTPStatus
Run the linter manually:
# Check for issues
uv run ruff check .
# Auto-fix what's possible
uv run ruff check --fix .
# Format code
uv run ruff format .
Project structure¶
src/ynaa_mcp/
app.py # FastMCP instance, AppContext, lifespan
server.py # Module wiring (imports all tool/prompt/resource modules)
client.py # Async YNAB API client (httpx)
models.py # Pydantic models and TypedDicts for API responses
errors.py # YNABAPIError, error handling utilities
budget_resolver.py # Automatic budget ID resolution
cache.py # TTL cache with monotonic clock expiration
rate_limiter.py # Sliding window rate limiter (200 req/hr)
converters.py # Milliunit-to-dollar and data transformations
knowledge.py # YNAB methodology resource loader
resources.py # MCP resource definitions
prompts.py # Core MCP prompt definitions
analysis.py # Analysis prompt templates (6 prompts)
workflows.py # Workflow guide prompt templates (6 prompts)
tools/
budgets.py # manage_budgets tool
accounts.py # manage_accounts tool
categories.py # manage_categories tool
transactions.py # manage_transactions tool
payees.py # manage_payees tool
months.py # manage_months tool
scheduled.py # manage_scheduled_transactions tool
cache.py # manage_cache tool
_helpers.py # Shared tool utilities
methodology/ # YNAB methodology markdown guides
templates/ # Prompt template markdown files
Each tool module registers a single consolidated tool (e.g., manage_budgets) that dispatches to private helper functions based on an action parameter.
Documentation¶
Documentation is built with MkDocs Material and auto-generated API reference via mkdocstrings.
# Start local preview server (hot-reload)
uv run --group docs mkdocs serve
# Build static site
uv run --group docs mkdocs build --strict
The docs/ directory mirrors the site navigation. Add new pages by creating a Markdown file and adding it to the nav: section in mkdocs.yml.
Pull request process¶
- Fork and clone the repository
- Create a branch from
dev(notmain) - Use conventional commits:
feat:-- new featurefix:-- bug fixdocs:-- documentation onlytest:-- adding or updating testschore:-- maintenance, dependencies, toolingrefactor:-- code change that doesn't fix a bug or add a feature- Ensure checks pass:
- Open a PR against
devwith a clear description of what changed and why
Questions?¶
Open an issue on GitHub if you have questions or run into problems.