# Renal Dose Adjustment Tool — Implementation Plan

> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.

**Goal:** Build a deterministic MCP tool that looks up FDA/guideline-approved renal dose adjustments for ~20 drugs, accepting CrCl or eGFR, returning structured dosing with per-drug DOI traceability.

**Architecture:** JSON drug database at `calculators/data/renal_dose_adjustments.json` + thin Python calculator at `calculators/renal_dose_adjustment.py`. Calculator loads JSON at module level, normalizes drug name, matches renal tier, returns `ClinicalResult` with structured value dict.

**Tech Stack:** Python 3.10+, Pydantic, pytest, Hypothesis

**Design doc:** `docs/plans/2026-03-07-renal-dose-adjustment-design.md`

---

### Task 1: Create JSON drug database with first 3 drugs (Vancomycin, Gabapentin, Metformin)

Start with 3 drugs from different classes to validate the schema before populating all 20.

**Files:**
- Create: `src/open_medicine/mcp/calculators/data/renal_dose_adjustments.json`

**Step 1: Create the data directory**

```bash
mkdir -p src/open_medicine/mcp/calculators/data
```

**Step 2: Write the JSON file with 3 seed drugs**

Create `src/open_medicine/mcp/calculators/data/renal_dose_adjustments.json` with entries for:

- **vancomycin** (antibiotic, IV, CrCl-based, requires TDM, DOI: 10.1093/cid/ciz895)
  - Tiers: >=50 no adjustment, 30-49 q12h, 10-29 q24-48h by levels, 0-9 by levels only
- **gabapentin** (analgesic/neuro, PO, CrCl-based, no TDM, DOI: 10.1016/S0140-6736(09)62099-3)
  - Tiers: >=60 no adjustment (300-600mg TID), 30-59 dose reduction (200-700mg BID), 15-29 (100-300mg daily), <15 contraindicated on dialysis (reduce proportionally)
- **metformin** (cardiovascular/endocrine, PO, eGFR-based, no TDM, DOI: 10.2337/dc24-S009)
  - Tiers: >=45 no adjustment (up to 2000mg/day), 30-44 dose reduction (max 1000mg/day), <30 contraindicated

Each drug entry must follow the schema from the design doc. Every field required. Tiers ordered descending by `min`. Every tier has `min`, `max` (null=infinity), `dose`, `adjustment_type`, `dialysis_note`, `warnings`.

**IMPORTANT:** The subagent building this task MUST research each drug's FDA label or guideline to get the exact renal adjustment tiers, thresholds, and DOIs. Use web search to verify FDA package inserts. Do NOT guess dosing data. The tiers above are approximate guidance — verify against actual sources.

**Step 3: Commit**

```bash
git add src/open_medicine/mcp/calculators/data/renal_dose_adjustments.json
git commit -m "feat: add renal dose adjustment JSON database with 3 seed drugs"
```

---

### Task 2: Write the calculator module with core lookup logic

**Files:**
- Create: `src/open_medicine/mcp/calculators/renal_dose_adjustment.py`

**Step 1: Write a minimal failing test**

Create `tests/test_renal_dose_adjustment.py`:

```python
from open_medicine.mcp.calculators.renal_dose_adjustment import (
    calculate_renal_dose_adjustment,
    RenalDoseAdjustmentParams,
    RenalMetric,
)

def test_vancomycin_normal_renal_function():
    params = RenalDoseAdjustmentParams(
        drug_name="vancomycin",
        renal_value=80.0,
        renal_metric=RenalMetric.CRCL,
    )
    result = calculate_renal_dose_adjustment(params)
    assert result.value["adjustment_type"] == "no_adjustment"
    assert result.value["drug_name"] == "vancomycin"
    assert result.value["metric_match"] is True
    assert result.evidence.source_doi != ""
```

**Step 2: Run test to verify it fails**

```bash
uv run python -m pytest tests/test_renal_dose_adjustment.py::test_vancomycin_normal_renal_function -v
```

Expected: FAIL — module does not exist yet.

**Step 3: Write the calculator module**

Create `src/open_medicine/mcp/calculators/renal_dose_adjustment.py`:

```python
import json
from pathlib import Path
from enum import Enum
from pydantic import BaseModel, Field
from open_medicine.foundation.base import ClinicalResult, Evidence


class RenalMetric(str, Enum):
    CRCL = "crcl"
    EGFR = "egfr"


class RenalDoseAdjustmentParams(BaseModel):
    """Parameters for renal dose adjustment lookup."""
    drug_name: str = Field(..., description="Generic drug name (e.g., 'vancomycin', 'gabapentin')")
    renal_value: float = Field(..., description="Renal function value (CrCl in mL/min or eGFR in mL/min/1.73m2)")
    renal_metric: RenalMetric = Field(..., description="Which renal metric is provided: 'crcl' or 'egfr'")


# Load drug database once at module level
_DATA_PATH = Path(__file__).parent / "data" / "renal_dose_adjustments.json"
with open(_DATA_PATH) as f:
    _DRUG_DB: dict = json.load(f)


def _get_unit(metric: RenalMetric) -> str:
    return "mL/min" if metric == RenalMetric.CRCL else "mL/min/1.73m2"


def _build_mismatch_warning(input_metric: RenalMetric, label_metric: str) -> str:
    label_name = "CrCl (Cockcroft-Gault)" if label_metric == "crcl" else "eGFR (CKD-EPI)"
    input_name = "eGFR (CKD-EPI)" if input_metric == RenalMetric.EGFR else "CrCl (Cockcroft-Gault)"
    return (
        f"FDA label specifies {label_name}. "
        f"{input_name} was provided. Values may diverge in elderly, obese, "
        f"or malnourished patients. Consider calculating {label_name} for this drug."
    )


def calculate_renal_dose_adjustment(params: RenalDoseAdjustmentParams) -> ClinicalResult:
    drug_key = params.drug_name.strip().lower()
    available_drugs = sorted(_DRUG_DB.keys())

    # Drug not found
    if drug_key not in _DRUG_DB:
        return ClinicalResult(
            value={"adjustment_type": "drug_not_found", "available_drugs": available_drugs},
            interpretation=f"Drug '{params.drug_name}' not found. Available drugs: {', '.join(available_drugs)}",
            evidence=Evidence(
                source_doi="N/A",
                level="N/A",
                description="Drug not in renal dose adjustment database.",
            ),
        )

    drug = _DRUG_DB[drug_key]
    label_metric = drug["label_renal_metric"]
    metric_match = params.renal_metric.value == label_metric
    mismatch_warning = None if metric_match else _build_mismatch_warning(params.renal_metric, label_metric)

    # Find matching tier (tiers ordered descending by min, first match wins)
    matched_tier = None
    for tier in drug["tiers"]:
        tier_max = tier["max"] if tier["max"] is not None else float("inf")
        if tier["min"] <= params.renal_value <= tier_max:
            matched_tier = tier
            break

    # Should not happen if tiers cover 0-infinity, but handle gracefully
    if matched_tier is None:
        matched_tier = drug["tiers"][-1]  # fallback to lowest tier

    # Build renal category label
    tier_max_label = str(matched_tier["max"]) if matched_tier["max"] is not None else "+"
    metric_label = "CrCl" if label_metric == "crcl" else "eGFR"
    renal_category = f"{metric_label} {matched_tier['min']}-{tier_max_label}"

    value = {
        "drug_name": drug["drug_name"],
        "brand_names": drug["brand_names"],
        "drug_class": drug["drug_class"],
        "renal_input": {
            "metric": params.renal_metric.value,
            "value": params.renal_value,
            "unit": _get_unit(params.renal_metric),
        },
        "label_renal_metric": label_metric,
        "metric_match": metric_match,
        "metric_mismatch_warning": mismatch_warning,
        "renal_category": renal_category,
        "normal_dose": drug["normal_dose"],
        "adjusted_dose": matched_tier["dose"],
        "adjustment_type": matched_tier["adjustment_type"],
        "requires_tdm": drug["requires_tdm"],
        "monitoring_parameters": drug.get("monitoring_parameters"),
        "dialysis_note": matched_tier.get("dialysis_note"),
        "hepatic_interaction_flag": drug.get("hepatic_interaction_flag", False),
        "warnings": matched_tier.get("warnings", []),
    }

    interpretation = (
        f"For {drug['drug_name']} ({', '.join(drug['brand_names'])}) with "
        f"{params.renal_metric.value.upper()} {params.renal_value} {_get_unit(params.renal_metric)}: "
        f"Recommended dose: {matched_tier['dose']}. "
        f"Adjustment: {matched_tier['adjustment_type']}."
    )
    if mismatch_warning:
        interpretation += f" WARNING: {mismatch_warning}"
    if matched_tier.get("dialysis_note"):
        interpretation += f" Dialysis: {matched_tier['dialysis_note']}."

    evidence = Evidence(
        source_doi=drug["source_doi"],
        level=drug["evidence_level"],
        description=drug["source_description"],
    )

    return ClinicalResult(
        value=value,
        interpretation=interpretation,
        evidence=evidence,
        fhir_code="29463-7",
        fhir_system="http://loinc.org",
        fhir_display="Medication dose",
    )
```

**Step 4: Run test to verify it passes**

```bash
uv run python -m pytest tests/test_renal_dose_adjustment.py::test_vancomycin_normal_renal_function -v
```

Expected: PASS

**Step 5: Commit**

```bash
git add src/open_medicine/mcp/calculators/renal_dose_adjustment.py tests/test_renal_dose_adjustment.py
git commit -m "feat: add renal dose adjustment calculator with core lookup logic"
```

---

### Task 3: Write comprehensive unit tests

**Files:**
- Modify: `tests/test_renal_dose_adjustment.py`

**Step 1: Add all unit tests**

Add these test functions to `tests/test_renal_dose_adjustment.py`:

```python
import json
from pathlib import Path
import pytest
from open_medicine.mcp.calculators.renal_dose_adjustment import (
    calculate_renal_dose_adjustment,
    RenalDoseAdjustmentParams,
    RenalMetric,
    _DRUG_DB,
)

# --- Happy path: one test per seed drug ---

def test_vancomycin_normal_renal_function():
    params = RenalDoseAdjustmentParams(drug_name="vancomycin", renal_value=80.0, renal_metric=RenalMetric.CRCL)
    result = calculate_renal_dose_adjustment(params)
    assert result.value["adjustment_type"] == "no_adjustment"
    assert result.value["drug_name"] == "vancomycin"
    assert result.value["metric_match"] is True
    assert result.evidence.source_doi != ""

def test_vancomycin_severe_impairment():
    params = RenalDoseAdjustmentParams(drug_name="vancomycin", renal_value=20.0, renal_metric=RenalMetric.CRCL)
    result = calculate_renal_dose_adjustment(params)
    assert result.value["adjustment_type"] in ("interval_extension", "dose_reduction", "use_with_caution")
    assert result.value["adjusted_dose"] != result.value["normal_dose"]

def test_gabapentin_moderate_impairment():
    params = RenalDoseAdjustmentParams(drug_name="gabapentin", renal_value=45.0, renal_metric=RenalMetric.CRCL)
    result = calculate_renal_dose_adjustment(params)
    assert result.value["adjustment_type"] in ("dose_reduction", "interval_extension")

def test_metformin_contraindicated():
    params = RenalDoseAdjustmentParams(drug_name="metformin", renal_value=20.0, renal_metric=RenalMetric.EGFR)
    result = calculate_renal_dose_adjustment(params)
    assert result.value["adjustment_type"] == "contraindicated"

# --- Tier boundary tests ---

def test_boundary_exact_min():
    """Value exactly at tier min should match that tier."""
    params = RenalDoseAdjustmentParams(drug_name="vancomycin", renal_value=30.0, renal_metric=RenalMetric.CRCL)
    result = calculate_renal_dose_adjustment(params)
    assert result.value["adjustment_type"] != "no_adjustment"  # 30 is in the reduced tier

def test_boundary_exact_max():
    """Value exactly at tier max should match that tier."""
    params = RenalDoseAdjustmentParams(drug_name="vancomycin", renal_value=49.0, renal_metric=RenalMetric.CRCL)
    result = calculate_renal_dose_adjustment(params)
    # 49 is in the 30-49 tier
    assert result.value["adjustment_type"] != "no_adjustment"

def test_boundary_just_above():
    """Value just above a tier boundary should be in the higher tier."""
    params = RenalDoseAdjustmentParams(drug_name="vancomycin", renal_value=50.0, renal_metric=RenalMetric.CRCL)
    result = calculate_renal_dose_adjustment(params)
    assert result.value["adjustment_type"] == "no_adjustment"

# --- Metric mismatch ---

def test_metric_mismatch_egfr_for_crcl_drug():
    """Providing eGFR for a drug whose label uses CrCl should flag mismatch."""
    params = RenalDoseAdjustmentParams(drug_name="vancomycin", renal_value=80.0, renal_metric=RenalMetric.EGFR)
    result = calculate_renal_dose_adjustment(params)
    assert result.value["metric_match"] is False
    assert result.value["metric_mismatch_warning"] is not None
    assert "CrCl" in result.value["metric_mismatch_warning"]

def test_metric_match_egfr_for_egfr_drug():
    """Providing eGFR for a drug whose label uses eGFR should match."""
    params = RenalDoseAdjustmentParams(drug_name="metformin", renal_value=60.0, renal_metric=RenalMetric.EGFR)
    result = calculate_renal_dose_adjustment(params)
    assert result.value["metric_match"] is True
    assert result.value["metric_mismatch_warning"] is None

# --- Drug not found ---

def test_drug_not_found():
    params = RenalDoseAdjustmentParams(drug_name="nonexistent_drug", renal_value=80.0, renal_metric=RenalMetric.CRCL)
    result = calculate_renal_dose_adjustment(params)
    assert result.value["adjustment_type"] == "drug_not_found"
    assert "available_drugs" in result.value

# --- Case insensitivity ---

@pytest.mark.parametrize("name", ["Vancomycin", "VANCOMYCIN", "vancomycin", " vancomycin "])
def test_case_insensitive_lookup(name):
    params = RenalDoseAdjustmentParams(drug_name=name, renal_value=80.0, renal_metric=RenalMetric.CRCL)
    result = calculate_renal_dose_adjustment(params)
    assert result.value["drug_name"] == "vancomycin"

# --- DOI present for every drug ---

@pytest.mark.parametrize("drug_name", list(_DRUG_DB.keys()))
def test_doi_present(drug_name):
    params = RenalDoseAdjustmentParams(drug_name=drug_name, renal_value=50.0, renal_metric=RenalMetric.CRCL)
    result = calculate_renal_dose_adjustment(params)
    assert result.evidence.source_doi != ""
    assert result.evidence.source_doi != "N/A"

# --- JSON schema validation ---

VALID_ADJUSTMENT_TYPES = {"no_adjustment", "dose_reduction", "interval_extension", "contraindicated", "use_with_caution"}
REQUIRED_DRUG_FIELDS = {"drug_name", "brand_names", "drug_class", "route", "label_renal_metric", "normal_dose", "requires_tdm", "hepatic_interaction_flag", "source_doi", "source_description", "evidence_level", "tiers"}
REQUIRED_TIER_FIELDS = {"min", "max", "dose", "adjustment_type"}

def test_json_schema_all_drugs_have_required_fields():
    for drug_key, drug in _DRUG_DB.items():
        missing = REQUIRED_DRUG_FIELDS - set(drug.keys())
        assert not missing, f"{drug_key} missing fields: {missing}"

def test_json_schema_all_tiers_have_required_fields():
    for drug_key, drug in _DRUG_DB.items():
        assert len(drug["tiers"]) >= 1, f"{drug_key} has no tiers"
        for i, tier in enumerate(drug["tiers"]):
            missing = REQUIRED_TIER_FIELDS - set(tier.keys())
            assert not missing, f"{drug_key} tier {i} missing fields: {missing}"

def test_json_schema_adjustment_types_valid():
    for drug_key, drug in _DRUG_DB.items():
        for i, tier in enumerate(drug["tiers"]):
            assert tier["adjustment_type"] in VALID_ADJUSTMENT_TYPES, \
                f"{drug_key} tier {i} has invalid adjustment_type: {tier['adjustment_type']}"

def test_json_schema_tiers_cover_zero_to_infinity():
    for drug_key, drug in _DRUG_DB.items():
        tiers = drug["tiers"]
        # Lowest tier must start at 0
        mins = [t["min"] for t in tiers]
        assert 0 in mins, f"{drug_key} tiers don't start at 0"
        # Highest tier must have max=null (infinity)
        has_infinity = any(t["max"] is None for t in tiers)
        assert has_infinity, f"{drug_key} tiers don't cover infinity"

def test_json_schema_no_overlapping_tiers():
    for drug_key, drug in _DRUG_DB.items():
        tiers = sorted(drug["tiers"], key=lambda t: t["min"])
        for i in range(len(tiers) - 1):
            current_max = tiers[i]["max"] if tiers[i]["max"] is not None else float("inf")
            next_min = tiers[i + 1]["min"]
            assert current_max < next_min or current_max == next_min - 1 or (current_max + 1 == next_min), \
                f"{drug_key}: tier {i} (max={current_max}) overlaps with tier {i+1} (min={next_min})"

def test_json_schema_doi_nonempty():
    for drug_key, drug in _DRUG_DB.items():
        assert drug["source_doi"].strip() != "", f"{drug_key} has empty DOI"
```

**Step 2: Run tests**

```bash
uv run python -m pytest tests/test_renal_dose_adjustment.py -v
```

Expected: All PASS (with the 3 seed drugs from Task 1).

**Step 3: Commit**

```bash
git add tests/test_renal_dose_adjustment.py
git commit -m "test: add comprehensive unit tests for renal dose adjustment"
```

---

### Task 4: Register in registry.py

**Files:**
- Modify: `src/open_medicine/mcp/registry.py`

**Step 1: Add import and registration**

At the top of `registry.py`, add the import (after the existing dosing imports):

```python
from open_medicine.mcp.calculators.renal_dose_adjustment import calculate_renal_dose_adjustment, RenalDoseAdjustmentParams
```

In the `CALCULATOR_REGISTRY` dict, add a new entry (after the anticoagulant dosing section):

```python
    # --- Renal Dose Adjustment (multi-drug lookup) ---
    "calculate_renal_dose_adjustment": RegisteredTool(
        description="Looks up FDA/guideline-approved renal dose adjustment for a drug based on CrCl or eGFR. Covers ~20 commonly renally-adjusted medications across antibiotics, analgesics, cardiovascular, antifungals, and antivirals.",
        pydantic_model=RenalDoseAdjustmentParams,
        execute_function=calculate_renal_dose_adjustment
    ),
```

**Step 2: Run full test suite to verify no import or registration conflicts**

```bash
uv run python -m pytest -v
```

Expected: All existing tests + new tests PASS.

**Step 3: Commit**

```bash
git add src/open_medicine/mcp/registry.py
git commit -m "feat: register renal dose adjustment calculator in MCP registry"
```

---

### Task 5: Add Hypothesis bounds test

**Files:**
- Modify: `tests/comparative_validation/test_bounds.py`

**Step 1: Add import and bounds test**

At the top of `test_bounds.py`, add:

```python
from open_medicine.mcp.calculators.renal_dose_adjustment import (
    calculate_renal_dose_adjustment, RenalDoseAdjustmentParams, RenalMetric, _DRUG_DB
)
```

At the bottom of the file, add:

```python
@given(
    st.builds(
        RenalDoseAdjustmentParams,
        drug_name=st.sampled_from(list(_DRUG_DB.keys())),
        renal_value=st.floats(min_value=0.0, max_value=200.0),
        renal_metric=st.sampled_from(list(RenalMetric)),
    )
)
def test_renal_dose_adjustment_never_crashes(params):
    result = calculate_renal_dose_adjustment(params)
    assert result is not None
    assert result.value is not None
    assert result.evidence.source_doi != ""
    assert result.value["adjustment_type"] in {
        "no_adjustment", "dose_reduction", "interval_extension",
        "contraindicated", "use_with_caution",
    }
```

**Step 2: Run bounds test**

```bash
uv run python -m pytest tests/comparative_validation/test_bounds.py::test_renal_dose_adjustment_never_crashes -v
```

Expected: PASS (Hypothesis runs ~100 random cases).

**Step 3: Commit**

```bash
git add tests/comparative_validation/test_bounds.py
git commit -m "test: add Hypothesis bounds test for renal dose adjustment"
```

---

### Task 6: Populate remaining ~17 drugs in the JSON database

**Files:**
- Modify: `src/open_medicine/mcp/calculators/data/renal_dose_adjustments.json`

**IMPORTANT:** This task requires web research for each drug. The subagent MUST:
1. Look up the FDA package insert or authoritative guideline for each drug
2. Extract the exact renal adjustment tiers (CrCl or eGFR thresholds)
3. Record the correct DOI for each source
4. Do NOT guess or approximate dosing data

**Drugs to add (grouped for efficient research):**

**Antibiotics (4 remaining):**
- gentamicin (IV, CrCl-based, requires TDM)
- levofloxacin (PO/IV, CrCl-based)
- cefepime (IV, CrCl-based)
- meropenem (IV, CrCl-based)
- piperacillin_tazobactam (IV, CrCl-based)

**Analgesics/Neuro (2 remaining):**
- pregabalin (PO, CrCl-based)
- morphine (PO/IV, CrCl-based, hepatic flag)

**Cardiovascular (3 remaining):**
- digoxin (PO, CrCl-based, requires TDM)
- atenolol (PO, CrCl-based)
- sotalol (PO, CrCl-based)

**Antifungals (1):**
- fluconazole (PO/IV, CrCl-based)

**Antivirals (2):**
- acyclovir (PO/IV, CrCl-based)
- valacyclovir (PO, CrCl-based)

**Other (4):**
- allopurinol (PO, CrCl-based)
- methotrexate (PO, CrCl/eGFR-based, hepatic flag)
- colchicine (PO, CrCl-based, hepatic flag)
- lithium (PO, CrCl-based, requires TDM)

Each drug entry must follow the exact same schema as the seed drugs. Every entry needs:
- `drug_name`, `brand_names`, `drug_class`, `route`
- `label_renal_metric` (crcl or egfr)
- `normal_dose`, `requires_tdm`, `monitoring_parameters`
- `hepatic_interaction_flag`
- `source_doi`, `source_description`, `evidence_level`
- `tiers` covering 0 to infinity with no gaps

**Step 1: Research and add all drugs**

Research each drug's FDA label renal adjustments via web search. Add entries to the JSON.

**Step 2: Run all tests to verify schema compliance**

```bash
uv run python -m pytest tests/test_renal_dose_adjustment.py -v
```

The schema validation tests will catch missing fields, gaps in tiers, invalid adjustment types, etc.

**Step 3: Run bounds test to verify no crashes**

```bash
uv run python -m pytest tests/comparative_validation/test_bounds.py::test_renal_dose_adjustment_never_crashes -v
```

**Step 4: Commit**

```bash
git add src/open_medicine/mcp/calculators/data/renal_dose_adjustments.json
git commit -m "feat: populate renal dose adjustment database with all 20 drugs"
```

---

### Task 7: Add per-drug happy path tests for all 20 drugs

**Files:**
- Modify: `tests/test_renal_dose_adjustment.py`

**Step 1: Add parametrized test for all drugs at multiple renal levels**

After the existing tests, add:

```python
# Test every drug at a normal renal value and a low renal value
@pytest.mark.parametrize("drug_name", list(_DRUG_DB.keys()))
def test_every_drug_normal_renal(drug_name):
    """Every drug should return a result at normal renal function."""
    drug = _DRUG_DB[drug_name]
    metric = RenalMetric.EGFR if drug["label_renal_metric"] == "egfr" else RenalMetric.CRCL
    params = RenalDoseAdjustmentParams(drug_name=drug_name, renal_value=90.0, renal_metric=metric)
    result = calculate_renal_dose_adjustment(params)
    assert result.value["adjustment_type"] == "no_adjustment"
    assert result.value["adjusted_dose"] == drug["normal_dose"]

@pytest.mark.parametrize("drug_name", list(_DRUG_DB.keys()))
def test_every_drug_low_renal(drug_name):
    """Every drug should return a non-normal result at very low renal function."""
    drug = _DRUG_DB[drug_name]
    metric = RenalMetric.EGFR if drug["label_renal_metric"] == "egfr" else RenalMetric.CRCL
    params = RenalDoseAdjustmentParams(drug_name=drug_name, renal_value=5.0, renal_metric=metric)
    result = calculate_renal_dose_adjustment(params)
    assert result.value["adjustment_type"] != "no_adjustment"
```

**Step 2: Run all tests**

```bash
uv run python -m pytest tests/test_renal_dose_adjustment.py -v
```

Expected: All PASS.

**Step 3: Run full test suite**

```bash
uv run python -m pytest -v
```

Expected: All tests pass, no regressions.

**Step 4: Commit**

```bash
git add tests/test_renal_dose_adjustment.py
git commit -m "test: add per-drug parametrized tests for all 20 drugs"
```

---

### Task 8: Final integration verification and commit

**Files:** None new — verification only.

**Step 1: Run full test suite**

```bash
uv run python -m pytest -v
```

Expected: All tests pass.

**Step 2: Verify MCP server can discover the new tool**

```bash
uv run python -c "
from open_medicine.mcp.registry import CALCULATOR_REGISTRY
tool = CALCULATOR_REGISTRY['calculate_renal_dose_adjustment']
print(f'Tool: {tool.description[:80]}...')
print(f'Schema keys: {list(tool.schema[\"properties\"].keys())}')
print('OK')
"
```

Expected: Prints tool description, schema keys (drug_name, renal_value, renal_metric), and "OK".

**Step 3: Quick smoke test**

```bash
uv run python -c "
from open_medicine.mcp.calculators.renal_dose_adjustment import (
    calculate_renal_dose_adjustment, RenalDoseAdjustmentParams, RenalMetric
)
result = calculate_renal_dose_adjustment(
    RenalDoseAdjustmentParams(drug_name='vancomycin', renal_value=25, renal_metric=RenalMetric.CRCL)
)
print(result.interpretation)
print(f'DOI: {result.evidence.source_doi}')
print(f'Adjusted dose: {result.value[\"adjusted_dose\"]}')
print(f'Warnings: {result.value[\"warnings\"]}')
"
```

Expected: Prints a coherent interpretation with dose adjustment, DOI, and warnings.
