()
| 139 | writer_done = anyio.Event() |
| 140 | |
| 141 | async def stdout_reader() -> None: |
| 142 | assert process.stdout, "Opened process is missing stdout" |
| 143 | |
| 144 | stdout = TextReceiveStream(process.stdout, encoding=server.encoding, errors=server.encoding_error_handler) |
| 145 | try: |
| 146 | async with read_stream_writer: |
| 147 | try: |
| 148 | # One line at a time; no read-ahead while a delivery is blocked. |
| 149 | buffer = "" |
| 150 | async for chunk in stdout: |
| 151 | lines = (buffer + chunk).split("\n") |
| 152 | buffer = lines.pop() |
| 153 | for line in lines: |
| 154 | try: |
| 155 | await read_stream_writer.send(_parse_line(line)) |
| 156 | except (anyio.ClosedResourceError, anyio.BrokenResourceError): |
| 157 | return # the session is gone; only the drain below remains |
| 158 | finally: |
| 159 | await _drain_stdout(process) |
| 160 | except anyio.ClosedResourceError: |
| 161 | pass # our own shutdown closed the stdout stream under the read |
| 162 | except (anyio.BrokenResourceError, ConnectionError): |
| 163 | # Teardown noise during shutdown, a real failure otherwise; either way |
| 164 | # the session sees clean closure when the read stream closes. |
| 165 | if not shutting_down: |
| 166 | logger.exception("Reading from the MCP server's stdout failed mid-session") |
| 167 | |
| 168 | async def stdin_writer() -> None: |
| 169 | assert process.stdin, "Opened process is missing stdin" |
nothing calls this directly
no test coverage detected