Skip to content

Adopt uv for dev workflow: dev dependencies are undeclared and CI is unreproducible locally #8

Description

@MicaelJarniac

Summary

pytest and pytest-asyncio are not declared anywhere in pyproject.toml. They exist only as inline pip install lines inside CI, so there is no way to set up a working dev environment from the repo alone — you have to read .github/workflows/ci.yml to discover what the test suite needs.

Adopting uv fixes this and brings qrz-mcp in line with adif-mcp, which already uses it.

The concrete gap

Cloning main and following the only documented setup (pip install -e ., from the Development section of the README) gives you a package with no test runner:

$ uv sync            # or: pip install -e .
$ uv run pytest -q
error: Failed to spawn: `pytest`
  Caused by: No such file or directory (os error 2)

The dependency exists, but only here (.github/workflows/ci.yml:30-33):

run: |
  python -m pip install --upgrade pip
  pip install -e .
  pip install pytest pytest-asyncio

Adding a dev group fixes it:

[dependency-groups]
dev = ["pytest>=8"]
$ uv sync --group dev
$ uv run pytest -q
43 passed

Note pytest-asyncio is installed by CI but never used — there is not a single async def or await in tests/. The suite passes without it. It can be dropped rather than carried into the new group.

Why uv specifically

adif-mcp already does this, so it is fleet alignment rather than a new pattern:

  • uv.lock committed
  • [dependency-groups] dev = [...] in pyproject.toml
  • CI on astral-sh/setup-uv@v1 with uv sync --group dev --frozen and uv run pytest -q
  • hatchling retained as the build backend

qrz-mcp is currently the odd one out. Of the siblings I checked (adif-mcp, hamqth-mcp, lotw-mcp, pota-mcp, solar-mcp), only adif-mcp has a lockfile — so this is fleet-wide drift, and qrz-mcp is a reasonable second adopter.

Beyond consistency, a committed uv.lock makes CI reproducible: right now pip install pytest resolves to whatever is newest on the day the job runs, so a bad upstream release breaks CI on a commit that changed nothing.

Proposed scope

  1. Add [dependency-groups] dev = ["pytest>=8"]; drop the unused pytest-asyncio.
  2. Commit uv.lock.
  3. Switch ci.yml to astral-sh/setup-uv + uv sync --group dev --frozen + uv run pytest.
  4. Update the README Development section to uv sync --group dev / uv run pytest.
  5. Keep hatchling as the build backend, and keep publish.yml on python -m build — matching adif-mcp. No packaging or release-path changes.

Deliberately out of scope

Changing the user-facing install instructions. uvx qrz-mcp does work — I verified it resolves 0.3.3 from PyPI, and it is genuinely convenient for MCP clients since it sidesteps "which Python is my client using". But making it the documented install adds a prerequisite for users who have never heard of uv, and adif-mcp still documents plain pip install adif-mcp. If a uv install path is wanted, it should sit alongside pip, as a separate change.

Note that step 4 touches the same README as #7, so this should land after that merges.

Verification

Everything above was run against a clone of main:

  • uv sync succeeds on the current pyproject.toml unmodified (84 packages locked).
  • Adding the dev group makes uv run pytest work: 43 passed.
  • Suite passes without pytest-asyncio.
  • The 3.10–3.13 matrix is preserved under uv via uv run --python 3.10 ... 3.13 — both ends verified passing, so CI coverage does not narrow. (Worth noting adif-mcp CI pins a single Python 3.11; qrz-mcp should keep its matrix rather than copy that part.)

uv 0.10.1.

Note

Happy to send a PR, but suggesting this waits — #4, #6 and #7 are open and unreviewed, and a CI/packaging change is the riskiest kind to review. The bug fixes are worth more and should clear first.

If uv is not wanted, the underlying defect still stands and can be fixed with plain [project.optional-dependencies] and no new tooling. The undeclared dev dependency is the actual bug; uv is one way to fix it.


AI disclosure

This issue was written by an AI agent (Claude, via OpenCode) working from a human-directed prompt.

All commands, outputs and version numbers quoted above were executed against a clone of main and the sibling repos, not asserted from model knowledge. The adif-mcp configuration was read from that repository rather than assumed.

Maintainer note: the scope boundary in this issue is a judgement call, not a finding — reasonable people could want the uvx install path documented too. The undeclared-dependency part is the objective defect; the rest is a preference worth your input.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions