Skip to content

workflow extra's anyio<4.7 pin blocks the mcp extra (anyio>=4.9) #139

Description

@LukasGold

osw[workflow] and osw[mcp] cannot be installed into the same environment.

  • osw[workflow] pins anyio>=4.4.0,<4.7
  • mcp>=2 requires anyio>=4.9 (anyio>=4.10 on Python 3.14+)

No anyio version satisfies both, so uv lock fails when both extras are resolvable together.

Why the anyio cap exists

prefect 2.20.25's GatherTaskGroup subclasses anyio's TaskGroup but does not
implement create_task, which newer anyio made abstract. Broken on anyio 4.14,
verified good on 4.6.2. prefect 2.20.25 is the final 2.x release, so no backport
is coming.

Current workaround

Applied in #133:

  1. [tool.uv] conflicts declares the mcp extra mutually exclusive with both the
    workflow extra and the dev dependency-group.
  2. mcp is excluded from the all extra and from the dev group, so the default
    resolution keeps the workflow-compatible anyio.
  3. The dev group lists prefect and anyio directly instead of inheriting them
    from osw[workflow]. Being in a conflict set, a self-referential osw[workflow]
    entry would only activate under --extra workflow, leaving a bare uv sync on
    anyio 4.14 (the version that breaks prefect). These pins now have to be kept in
    sync with the workflow extra by hand.
  4. The pytest stack moved into its own test group, since dev conflicts with the
    mcp extra and there would otherwise be no environment containing both pytest
    and mcp.
  5. src/osw/mcp is excluded from ty, since its imports are unresolvable in the
    default dev environment.
  6. The MCP unit tests use importorskip, so they skip in the default suite.

Cost

The MCP server module is neither type-checked nor unit-tested in the default
environment. Its tests pass (32 passed against mcp==2.0.0) but only when run
explicitly:

uv sync --extra mcp --group test --no-dev
uv run --extra mcp --group test --no-dev --no-sync python -m pytest tests/test_mcp_*.py

CI does not currently run that second environment.

What to check

Whether the anyio cap is still needed, either because a newer anyio restored the
non-abstract create_task path, or because the workflow extra can move to
prefect 3.x. If the cap can be lifted, revert all six workarounds above and put
mcp back into all and dev.

Activity

  1. LukasGold commented on Aug 28, 2026

    @LukasGold
    ContributorAuthor

    The cap is still needed, but <4.7 is seven minors too tight

    Bisected prefect==2.20.25 against every anyio minor (py3.12, exercising prefect's own gather()):

    anyio GatherTaskGroup.__abstractmethods__ result
    4.6.2, 4.7.0, 4.9.0, 4.10.0, 4.11.0, 4.12.0, 4.13.0 [] works
    4.14.0, 4.14.2 (latest) ['create_task'] TypeError: Can't instantiate abstract class GatherTaskGroup

    The break is exactly at 4.14.0, which added create_task() to the TaskGroup ABC and changed start_soon() to return a TaskHandle (anyio changelog, agronholm/anyio#1098). That promoted create_task into the abstract set, so prefect's external subclass can no longer be instantiated. Not reverted in 4.14.1 or 4.14.2.

    This resolves the conflict

    mcp 2.1.1 requires anyio>=4.9. Its stricter anyio>=4.10 clause is gated on python_version >= '3.14', and osw is requires-python = ">=3.10,<3.14", so that clause never applies here.

    That leaves a non-empty window: anyio>=4.9,<4.14. Verified by co-resolving all three:

    prefect 2.20.25 + mcp 2.1.1 + anyio 4.13.0  ->  both import, GatherTaskGroup concrete
    

    No move to prefect 3.x is required, and no osw-python code change is required.

    Verified

    Branch fix/widen-anyio-pin changes the workflow extra to anyio>=4.9,<4.14:

    • uv lock resolves, anyio 4.6.2.post1 -> 4.13.0, prefect stays 2.20.25
    • full default suite: 36 passed, 1 skipped
    • workflow tests pass (test_notify_teams, test_tags_str_to_list, test_deploy_serve); test_deploy_runner is skipped by a pre-existing unconditional marker

    Follow-up on feat/mcp-server

    Five of the six workarounds from #133 become unnecessary once this lands:

    1. [tool.uv] conflicts (mcp vs workflow, mcp vs dev)
    2. mcp excluded from the all extra
    3. dev listing prefect/anyio directly instead of osw[workflow] (removes the hand-sync hazard)
    4. the separate test group, which existed only because dev conflicted with the mcp extra
    5. the importorskip guards in the MCP tests

    The sixth (the ty exclude for src/osw/mcp) is already undone on that branch.

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