feat: add DORA query tools for agent reasoning #31

Merged
ldraney merged 1 commit from 28-dora-query-tools into main 2026-06-13 20:05:40 +00:00
Owner

Summary

Adds four new MCP tools that let agents query PR velocity, lead time, rework rate, and deployment frequency directly from Forgejo PR data. This enables data-driven DORA metric reasoning without external tooling.

Changes

  • src/forgejo_mcp/tools/dora.py (new) -- Four DORA query tools:
    • get_pr_velocity -- merge count and merges/week over a configurable time window
    • get_pr_lead_time -- min/max/mean/median hours from PR open to merge
    • get_rework_rate -- percentage of PRs with REQUEST_CHANGES reviews (proxy for change failure rate)
    • get_deployment_frequency -- DORA tier classification (elite/high/medium/low) based on merge cadence
  • src/forgejo_mcp/tools/__init__.py -- Register the new dora module in register_all_tools()
  • tests/test_dora_tools.py (new) -- 20 unit tests covering all four tools: correct calculations, pagination, empty repos, time window filtering, API error handling, DORA tier classification, median edge cases, review pagination

Test Plan

  • 20 unit tests pass (pytest tests/test_dora_tools.py -v)
  • Full test suite passes (24/24 including existing tests)
  • ruff check clean
  • ruff format clean
  • Manual: call get_pr_velocity("ldraney", "forgejo-mcp", 90) and verify structured output
  • Manual: call get_deployment_frequency("ldraney", "forgejo-mcp", 30) and verify DORA tier

Review Checklist

  • Follows existing tool patterns (Annotated params, json.dumps return, _error_response catch)
  • Handles pagination for repos with many PRs
  • Time window parameter with sensible default (30 days)
  • Returns structured JSON agents can reason about
  • New module registered in __init__.py
  • No changes to existing tools or tests
  • Closes #28
  • Pattern reference: issue #25 (update_issue tool), issue #10 (batch tool addition)
  • DORA framework measurement automation roadmap
## Summary Adds four new MCP tools that let agents query PR velocity, lead time, rework rate, and deployment frequency directly from Forgejo PR data. This enables data-driven DORA metric reasoning without external tooling. ## Changes - **`src/forgejo_mcp/tools/dora.py`** (new) -- Four DORA query tools: - `get_pr_velocity` -- merge count and merges/week over a configurable time window - `get_pr_lead_time` -- min/max/mean/median hours from PR open to merge - `get_rework_rate` -- percentage of PRs with REQUEST_CHANGES reviews (proxy for change failure rate) - `get_deployment_frequency` -- DORA tier classification (elite/high/medium/low) based on merge cadence - **`src/forgejo_mcp/tools/__init__.py`** -- Register the new `dora` module in `register_all_tools()` - **`tests/test_dora_tools.py`** (new) -- 20 unit tests covering all four tools: correct calculations, pagination, empty repos, time window filtering, API error handling, DORA tier classification, median edge cases, review pagination ## Test Plan - [x] 20 unit tests pass (`pytest tests/test_dora_tools.py -v`) - [x] Full test suite passes (24/24 including existing tests) - [x] `ruff check` clean - [x] `ruff format` clean - [ ] Manual: call `get_pr_velocity("ldraney", "forgejo-mcp", 90)` and verify structured output - [ ] Manual: call `get_deployment_frequency("ldraney", "forgejo-mcp", 30)` and verify DORA tier ## Review Checklist - [x] Follows existing tool patterns (Annotated params, json.dumps return, _error_response catch) - [x] Handles pagination for repos with many PRs - [x] Time window parameter with sensible default (30 days) - [x] Returns structured JSON agents can reason about - [x] New module registered in `__init__.py` - [x] No changes to existing tools or tests ## Related Notes - Closes #28 - Pattern reference: issue #25 (update_issue tool), issue #10 (batch tool addition) - DORA framework measurement automation roadmap
feat: add DORA query tools for agent reasoning
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/pr/woodpecker Pipeline was successful
4119291276
Add four new MCP tools that let agents query PR velocity, lead time,
rework rate, and deployment frequency from Forgejo PR data. These
enable data-driven DORA metric reasoning without external tooling.

Tools added:
- get_pr_velocity: merge count and merges/week over a time window
- get_pr_lead_time: min/max/mean/median hours from PR open to merge
- get_rework_rate: percentage of PRs with REQUEST_CHANGES reviews
- get_deployment_frequency: DORA tier classification (elite/high/medium/low)

All tools support configurable lookback windows (default 30 days),
handle pagination for repos with many PRs, and return structured
JSON that agents can reason about directly.

Closes #28

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Author
Owner

PR #31 Review

DOMAIN REVIEW

Stack: Python 3.10+ / FastMCP / Pydantic / Forgejo SDK

This PR adds four DORA metric query tools in a new src/forgejo_mcp/tools/dora.py module (390 lines) with 20 unit tests in tests/test_dora_tools.py (495 lines). Registration is correctly wired into __init__.py.

Pattern compliance: The new module follows established conventions exactly -- Annotated params with Field(description=...), json.dumps(indent=2) returns, _error_response(exc) error handling, consistent imports from ..server. The @mcp.tool() decorator usage matches workflows.py.

Pagination: _fetch_merged_prs correctly paginates through closed PRs and filters client-side by merged_at within the time window. The get_rework_rate tool also paginates reviews per PR. Both use the same _PAGE_SIZE = 50 constant and the len(batch) < _PAGE_SIZE stop condition, consistent with the pagination pattern in set_label.

Time handling: _parse_iso handles both Z and +00:00 suffixes, with a comment explaining Python 3.10 compatibility. _resolve_time_window correctly uses UTC-aware datetimes. The max(days / 7, 1) and max(days, 1) guards prevent division-by-zero at boundary values.

DORA tier classification: The thresholds in get_deployment_frequency are mathematically sound -- >1/day (elite), >=1/7/day (high), >=1/30/day (medium), else low. The boundary values are well-tested (tests cover all four tiers).

Median calculation: The manual median in get_pr_lead_time is correct for both odd and even counts, with dedicated tests for both cases. Could use statistics.median() from stdlib but the manual implementation is sound and avoids an import.

Test quality: 20 unit tests covering:

  • Correct merge count and velocity calculation
  • Empty repo edge case
  • Merges-per-week arithmetic
  • Pagination across multiple pages
  • Time window exclusion filtering
  • API error propagation
  • Lead time min/max/mean/median
  • No-data graceful handling
  • Single-PR and odd-count median edge cases
  • Rework detection via REQUEST_CHANGES reviews
  • Zero-rework path
  • Review pagination (50+ reviews)
  • All four DORA tiers
  • Daily breakdown population
  • API timeout error handling

Tests mock at the correct level (forgejo_mcp.tools.dora.get_client) and use side_effect for multi-page scenarios, consistent with the test_set_label_pagination.py pattern.

BLOCKERS

None.

  • No unvalidated user input -- all params are typed via Pydantic Annotated[str/int, Field()]
  • No secrets or credentials in code
  • No DRY violations in auth paths
  • Test coverage is thorough (20 tests across 4 tools)
  • No security-relevant code paths introduced

NITS

  1. Dead fallback in _fetch_merged_prs (line 57 of dora.py):

    merged_at = _parse_iso(pr.get("merged_at") or pr.get("merge_base"))
    

    merge_base is a git ref/SHA string, not a timestamp. If merged_at is None, _parse_iso will fail on the SHA and return None, so the PR gets skipped correctly -- no functional bug. But the or pr.get("merge_base") fallback is misleading dead code. Consider removing it:

    merged_at = _parse_iso(pr.get("merged_at"))
    
  2. Import-inside-function pattern in tests: Each test method imports the tool function inside the method body (from forgejo_mcp.tools.dora import get_pr_velocity). This works but is unconventional. Module-level imports at the top of the test file (or class level) would be more standard. Non-blocking since it works and the existing integration tests in this repo use a similar pattern with direct function imports.

  3. No README update: The README lists all MCP tools. The four new DORA tools are not documented there. This is a follow-up item, not a blocker for this PR -- tracking as a separate doc update is fine (same pattern as issue #26 which caught missing update_issue docs).

  4. statistics.median() available: Python stdlib statistics.median() handles both odd/even counts. The manual implementation is correct but the stdlib function would be more concise and battle-tested. Minor preference, not blocking.

SOP COMPLIANCE

  • PR body has: Summary, Changes, Test Plan, Related -- all present and detailed
  • Test Plan includes both automated (20 unit tests) and manual verification steps
  • No secrets, .env files, or credentials committed
  • No unnecessary file changes -- exactly 3 files changed, all directly relevant
  • Commit message follows conventional commits (feat: add DORA query tools...)
  • PR references parent issue (Closes #28)

PROCESS OBSERVATIONS

  • This PR adds read-only query tools (no mutations) which is low change-failure risk
  • The merge-as-deployment-proxy approach is a pragmatic starting point for DORA metrics
  • The structured JSON output enables agent reasoning, which is the stated goal
  • 889 additions with 1 deletion is a clean additive change with zero regression risk to existing tools

VERDICT: APPROVED

## PR #31 Review ### DOMAIN REVIEW **Stack:** Python 3.10+ / FastMCP / Pydantic / Forgejo SDK This PR adds four DORA metric query tools in a new `src/forgejo_mcp/tools/dora.py` module (390 lines) with 20 unit tests in `tests/test_dora_tools.py` (495 lines). Registration is correctly wired into `__init__.py`. **Pattern compliance:** The new module follows established conventions exactly -- `Annotated` params with `Field(description=...)`, `json.dumps(indent=2)` returns, `_error_response(exc)` error handling, consistent imports from `..server`. The `@mcp.tool()` decorator usage matches `workflows.py`. **Pagination:** `_fetch_merged_prs` correctly paginates through closed PRs and filters client-side by `merged_at` within the time window. The `get_rework_rate` tool also paginates reviews per PR. Both use the same `_PAGE_SIZE = 50` constant and the `len(batch) < _PAGE_SIZE` stop condition, consistent with the pagination pattern in `set_label`. **Time handling:** `_parse_iso` handles both `Z` and `+00:00` suffixes, with a comment explaining Python 3.10 compatibility. `_resolve_time_window` correctly uses UTC-aware datetimes. The `max(days / 7, 1)` and `max(days, 1)` guards prevent division-by-zero at boundary values. **DORA tier classification:** The thresholds in `get_deployment_frequency` are mathematically sound -- `>1/day` (elite), `>=1/7/day` (high), `>=1/30/day` (medium), else low. The boundary values are well-tested (tests cover all four tiers). **Median calculation:** The manual median in `get_pr_lead_time` is correct for both odd and even counts, with dedicated tests for both cases. Could use `statistics.median()` from stdlib but the manual implementation is sound and avoids an import. **Test quality:** 20 unit tests covering: - Correct merge count and velocity calculation - Empty repo edge case - Merges-per-week arithmetic - Pagination across multiple pages - Time window exclusion filtering - API error propagation - Lead time min/max/mean/median - No-data graceful handling - Single-PR and odd-count median edge cases - Rework detection via REQUEST_CHANGES reviews - Zero-rework path - Review pagination (50+ reviews) - All four DORA tiers - Daily breakdown population - API timeout error handling Tests mock at the correct level (`forgejo_mcp.tools.dora.get_client`) and use `side_effect` for multi-page scenarios, consistent with the `test_set_label_pagination.py` pattern. ### BLOCKERS None. - No unvalidated user input -- all params are typed via Pydantic `Annotated[str/int, Field()]` - No secrets or credentials in code - No DRY violations in auth paths - Test coverage is thorough (20 tests across 4 tools) - No security-relevant code paths introduced ### NITS 1. **Dead fallback in `_fetch_merged_prs` (line 57 of dora.py):** ```python merged_at = _parse_iso(pr.get("merged_at") or pr.get("merge_base")) ``` `merge_base` is a git ref/SHA string, not a timestamp. If `merged_at` is `None`, `_parse_iso` will fail on the SHA and return `None`, so the PR gets skipped correctly -- no functional bug. But the `or pr.get("merge_base")` fallback is misleading dead code. Consider removing it: ```python merged_at = _parse_iso(pr.get("merged_at")) ``` 2. **Import-inside-function pattern in tests:** Each test method imports the tool function inside the method body (`from forgejo_mcp.tools.dora import get_pr_velocity`). This works but is unconventional. Module-level imports at the top of the test file (or class level) would be more standard. Non-blocking since it works and the existing integration tests in this repo use a similar pattern with direct function imports. 3. **No README update:** The README lists all MCP tools. The four new DORA tools are not documented there. This is a follow-up item, not a blocker for this PR -- tracking as a separate doc update is fine (same pattern as issue #26 which caught missing `update_issue` docs). 4. **`statistics.median()` available:** Python stdlib `statistics.median()` handles both odd/even counts. The manual implementation is correct but the stdlib function would be more concise and battle-tested. Minor preference, not blocking. ### SOP COMPLIANCE - [x] PR body has: Summary, Changes, Test Plan, Related -- all present and detailed - [x] Test Plan includes both automated (20 unit tests) and manual verification steps - [x] No secrets, .env files, or credentials committed - [x] No unnecessary file changes -- exactly 3 files changed, all directly relevant - [x] Commit message follows conventional commits (`feat: add DORA query tools...`) - [x] PR references parent issue (`Closes #28`) ### PROCESS OBSERVATIONS - This PR adds read-only query tools (no mutations) which is low change-failure risk - The merge-as-deployment-proxy approach is a pragmatic starting point for DORA metrics - The structured JSON output enables agent reasoning, which is the stated goal - 889 additions with 1 deletion is a clean additive change with zero regression risk to existing tools ### VERDICT: APPROVED
ldraney deleted branch 28-dora-query-tools 2026-06-13 20:05:40 +00:00
Sign in to join this conversation.
No description provided.