Drive the ``mode='auto'`` connect-time policy on ``session``. Probes ``server/discover`` once (twice if the server names a mutual modern version via -32022), then either ``adopt()``s the result or falls back to ``initialize()``. Idempotent only in the sense that one of ``session.dis
(session: ClientSession)
| 39 | |
| 40 | |
| 41 | async def negotiate_auto(session: ClientSession) -> None: |
| 42 | """Drive the ``mode='auto'`` connect-time policy on ``session``. |
| 43 | |
| 44 | Probes ``server/discover`` once (twice if the server names a mutual |
| 45 | modern version via -32022), then either ``adopt()``s the result or falls |
| 46 | back to ``initialize()``. Idempotent only in the sense that one of |
| 47 | ``session.discover_result`` / ``session.initialize_result`` is set on |
| 48 | return. |
| 49 | |
| 50 | Raises: |
| 51 | MCPError: The server is modern-only and shares no version with this |
| 52 | client (-32022 with a disjoint ``supported`` list). |
| 53 | Exception: Any transport/network error from the probe propagates as-is. |
| 54 | """ |
| 55 | version = LATEST_MODERN_VERSION |
| 56 | for attempt in range(2): |
| 57 | try: |
| 58 | raw = await session.send_discover(version) |
| 59 | except MCPError as e: |
| 60 | if e.code == UNSUPPORTED_PROTOCOL_VERSION: |
| 61 | supported = _parse_supported(e.error.data) |
| 62 | mutual = [v for v in MODERN_PROTOCOL_VERSIONS if v in (supported or ())] |
| 63 | if mutual and attempt == 0: |
| 64 | version = mutual[-1] |
| 65 | continue |
| 66 | if supported is not None and not any(v in HANDSHAKE_PROTOCOL_VERSIONS for v in supported): |
| 67 | raise # server is modern-only and disjoint — real incompatibility |
| 68 | await session.initialize() # every other rpc-error → legacy (the denylist) |
| 69 | return |
| 70 | # any other exception (httpx.TransportError, ConnectionError, anyio errors, |
| 71 | # RuntimeError from adopt) → propagate |
| 72 | try: |
| 73 | result = types.DiscoverResult.model_validate(raw) |
| 74 | except ValidationError: |
| 75 | await session.initialize() # unparseable result → not modern evidence |
| 76 | return |
| 77 | session.adopt(result) |
| 78 | return |
| 79 | raise AssertionError("unreachable") # pragma: no cover — loop body always returns or raises |