Skip to content

feat(mcp): add drt_doctor tool + drt_run_sync(compute_diff=True) CLI parity#628

Merged
masukai merged 2 commits into
mainfrom
feat/mcp-doctor-diff
Jun 9, 2026
Merged

feat(mcp): add drt_doctor tool + drt_run_sync(compute_diff=True) CLI parity#628
masukai merged 2 commits into
mainfrom
feat/mcp-doctor-diff

Conversation

@masukai

@masukai masukai commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

Summary

Two MCP tools that had been shipped on the CLI for months but weren't reachable from Claude / Cursor:

  1. drt_doctor — mirrors drt doctor (v0.7.0, good first issue: add drt doctor command (environment check) #264)
  2. drt_run_sync(compute_diff=True) — mirrors drt run --dry-run --diff (v0.7.1, feat: drt diff — show record-level diff in dry-run mode #413)

This closes the visible part of the MCP-vs-CLI gap. Part of the broader skill/MCP refresh sequence triggered by "skillやMCPに最新の情報が載っていないのは困ります".

What drt_doctor returns

{
  "passed": true,
  "checks": [
    {"category": "runtime", "name": "Python version", "ok": true, "message": "3.12.12 ✅"},
    {"category": "runtime", "name": "drt version", "ok": true, "message": "0.7.8"},
    {"category": "project", "name": "Project file", "ok": true, "message": ""},
    {"category": "project", "name": "Profile", "ok": true, "message": "✅ default"},
    {"category": "project", "name": "Syncs", "ok": true, "message": "✅ 1 sync file"},
    {"category": "extras", "name": "bigquery", "ok": false, "message": "❌ not installed (pip install drt-core[bigquery])"},
    ...
  ]
}

passed is False only when a required check fails (project file / profile / Python version); missing optional extras don't flip it. Reuses the _check_* helpers in drt/cli/doctor.py — no CLI changes, no duplicated orchestration.

What drt_run_sync adds

Two new parameters, both default False/20:

drt_run_sync(
    sync_name: str,
    dry_run: bool = False,
    compute_diff: bool = False,    # NEW
    diff_limit: int = 20,           # NEW
)

When set with dry_run=True, the response carries a structured diff field via the existing cli/output.diff_to_dict serialiser, so an LLM can preview added / updated / deleted / unchanged records for queryable destinations before committing.

Contract: compute_diff=True without dry_run=True returns a structured error rather than running against a live destination. Matches the CLI's --diff requires --dry-run contract.

Docstring + README catch-up

Tests

5 new tests in tests/unit/test_mcp.py:

Test Coverage
test_server_lists_drt_doctor_tool Registration check
test_run_sync_compute_diff_requires_dry_run Contract error path (no live-dest accident)
test_doctor_returns_structured_report Shape contract — {passed, checks} + per-check keys
test_doctor_passes_on_well_formed_project Happy path on fixture project + fake ~/.drt/profiles.yml
test_doctor_fails_without_project_file passed=False path

Test plan

  • pytest tests/unit/test_mcp.py — 19 passed (14 existing + 5 new)
  • Full unit + contracts: 1466 passed
  • make lint — ruff + mypy clean
  • CI green on 3.10–3.13 + CodeQL

Out of scope (deliberate)

  • drt_list_connectors doesn't yet enumerate Mixpanel / Amazon S3 / Twilio / Intercom / Email SMTP / Salesforce Bulk / Staged Upload / Google Ads / Amplitude / Zendesk — a separate drift. Keeping this PR focused on the two flagged gaps.
  • No new MCP tools beyond the two listed; the existing surface is preserved.

Sequence

This is the MCP leg of the skill / MCP refresh:

  1. ✅ /drt-debug refresh — chore(skill): refresh /drt-debug — catch up to v0.7+ features (3-month gap) #625
  2. ✅ /drt-init refresh — chore(skill): refresh /drt-init — catch up to v0.7+ sources + flows (2-month gap) #626
  3. ✅ /drt-migrate refresh — chore(skill): refresh /drt-migrate — add mirror/replace modes + post-v0.7 mappings #627
  4. ⏳ /drt-create-sync GCS+Azure follow-up (after feat(destinations): add Google Cloud Storage destination (csv/json/jsonl/parquet + gzip) (closes #169) #623 feat(destinations): add Azure Blob Storage destination (csv/json/jsonl/parquet + gzip) (closes #170) #624 merge)
  5. This PR — MCP refresh

🤖 Generated with Claude Code

@masukai masukai requested a review from yodakanohoshi June 9, 2026 12:13
@codecov

codecov Bot commented Jun 9, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

masukai and others added 2 commits June 9, 2026 22:23
…parity

The MCP server picked up two pieces that had been shipped on the CLI
for months but weren't reachable from Claude / Cursor:

1. **`drt_doctor`** — mirrors `drt doctor` (v0.7.0+) and returns a
   structured `{"passed": bool, "checks": [...]}` report. Categories:
   - runtime (Python version + drt version)
   - project (drt_project.yml, profile, syncs/)
   - extras (per-extra install status)
   - env (relevant env vars + masked values)
   ``passed`` is False only when a required check fails (project file
   / profile / Python version); missing optional extras don't flip it.

   Implementation reuses the ``_check_*`` helpers in ``drt/cli/doctor.py``
   to avoid duplicating orchestration logic. No CLI changes.

2. **`drt_run_sync(compute_diff=True, diff_limit=20)`** — matches the
   `drt run --dry-run --diff` CLI (#413, v0.7.1+). When set with
   ``dry_run=True``, the response carries a structured ``diff`` field
   serialised via the existing ``cli/output.diff_to_dict`` helper, so
   the LLM can preview added / updated / deleted / unchanged records
   for queryable destinations before committing.

   ``compute_diff=True`` without ``dry_run=True`` returns a structured
   error rather than silently running against a live destination —
   matches the CLI's `--diff requires --dry-run` contract.

Docstring + README catch-up:
- Module docstring lists the now-complete tool surface (9 tools)
- `drt_get_history` — registered in code since #445 but missing from
  the docstring — is now listed there too
- README.md + README.ja.md MCP tool tables updated to the 9-tool list
  (was 6, missing run_test / get_history / doctor)
- CHANGELOG `[Unreleased] → Changed (Internal)` entry

Tests:
5 new tests in tests/unit/test_mcp.py:
- `test_server_lists_drt_doctor_tool` — registration check
- `test_run_sync_compute_diff_requires_dry_run` — contract error path
- `test_doctor_returns_structured_report` — shape contract
- `test_doctor_passes_on_well_formed_project` — happy path on the
  fixture project + a fake HOME with profiles.yml
- `test_doctor_fails_without_project_file` — `passed=False` path

Existing 14 MCP tests unchanged. Full unit + contracts suite: 1466
passed. lint + mypy clean.

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
…ge above codecov target)

codecov/patch on the prior commit hit 81.81% (target 85.86%) — the
gate passed but below the bar. All uncovered lines were in
``drt_run_sync``: the existing tests only exercised error paths
(``compute_diff requires dry_run`` + the unknown-sync error path
wasn't even tested at all), so the success branch (load_project /
run_sync / response with optional diff) ran zero times.

Adds 3 tests that drive the success branch via monkeypatched engine
+ factory + serializer doubles:

1. `test_run_sync_returns_error_for_unknown_sync` — unknown sync_name
   short-circuit (line 114). Needed a load_profile mock too because
   the profile check fires before the sync match in the flow.

2. `test_run_sync_compute_diff_threads_diff_into_response` —
   compute_diff=True + dry_run=True success path. Verifies the diff
   field is built via diff_to_dict and the value flows through to
   the response.

3. `test_run_sync_dry_run_without_compute_diff_omits_diff_field` —
   compute_diff=False (default) success path. Verifies the diff
   branch is skipped and ``response`` doesn't carry a diff key.

drt/mcp/server.py file coverage: 71% → 83%. My patch diff coverage
should now hit ~100% — remaining unhit lines are all pre-existing
untested tools (drt_run_test internals at 206-224, drt_get_history
body at 300-305, drt_list_connectors body at 360, outer ``def run()``
at 473-474, fastmcp ImportError fallback at 29-30) — none of which
are in this PR's diff.

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
@masukai masukai force-pushed the feat/mcp-doctor-diff branch from 7c07b85 to 8b175f8 Compare June 9, 2026 13:27
@masukai masukai merged commit 46dedc1 into main Jun 9, 2026
8 checks passed
@masukai masukai deleted the feat/mcp-doctor-diff branch June 9, 2026 22:13
@github-actions github-actions Bot locked and limited conversation to collaborators Jun 9, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant