Start the runner and wait for the session to be ready. Returns: The initialized ClientSession. Raises: ConnectionError: If session creation fails.
(self)
| 107 | return self._task is not None and not self._task.done() |
| 108 | |
| 109 | async def start(self) -> ClientSession: |
| 110 | """Start the runner and wait for the session to be ready. |
| 111 | |
| 112 | Returns: |
| 113 | The initialized ClientSession. |
| 114 | |
| 115 | Raises: |
| 116 | ConnectionError: If session creation fails. |
| 117 | """ |
| 118 | async with self._task_lock: |
| 119 | if self._session: |
| 120 | logger.debug( |
| 121 | 'Session has already been created, returning existing session' |
| 122 | ) |
| 123 | return self._session |
| 124 | |
| 125 | if self._close_event.is_set(): |
| 126 | raise ConnectionError( |
| 127 | 'Failed to create MCP session: session already closed' |
| 128 | ) |
| 129 | |
| 130 | if not self._task: |
| 131 | self._task = asyncio.create_task(self._run()) |
| 132 | |
| 133 | def _retrieve_exception(t: asyncio.Task): |
| 134 | if not t.cancelled(): |
| 135 | t.exception() |
| 136 | |
| 137 | self._task.add_done_callback(_retrieve_exception) |
| 138 | |
| 139 | await self._ready_event.wait() |
| 140 | |
| 141 | if self._task.cancelled(): |
| 142 | raise ConnectionError('Failed to create MCP session: task cancelled') |
| 143 | |
| 144 | if self._task.done() and self._task.exception(): |
| 145 | raise ConnectionError( |
| 146 | f'Failed to create MCP session: {self._task.exception()}' |
| 147 | ) from self._task.exception() |
| 148 | |
| 149 | # Pre-fix code returned `self._session` here directly (typed as |
| 150 | # ClientSession even though it could in theory be None). Adding an |
| 151 | # explicit None check is safer but introduces a new exception path, |
| 152 | # so we gate it behind the feature flag to keep flag-OFF byte-for-byte |
| 153 | # compatible with pre-fix behavior. |
| 154 | if ( |
| 155 | is_feature_enabled(FeatureName._MCP_GRACEFUL_ERROR_HANDLING) # pylint: disable=protected-access |
| 156 | and self._session is None |
| 157 | ): |
| 158 | raise ConnectionError('Failed to create MCP session: unknown error') |
| 159 | |
| 160 | return self._session # type: ignore[return-value] |
| 161 | |
| 162 | async def _run_guarded(self, coro: Coroutine[Any, Any, _T]) -> _T: |
| 163 | """Run a coroutine while monitoring the background session task. |