Repository navigation
Conversation
On Windows, PHP waits for data on a pipe in a loop of 3,200,000 usleep(10) calls, also in non-blocking mode. One call takes about 15 ms there, so fgets() on an idle pipe blocks for about 13.5 hours and then reports a false EOF, which ends listen(). Reading only after stream_select() reports input stays out of that loop. It needs PHP 8.5, where stream_select() stopped reporting idle pipes as readable (php/php-src#16889). stream_select() throws a ValueError on a stream it cannot select, like php://memory, instead of returning false.
dg
requested review from
CodeWithKyrian,
Nyholm,
chr-hertel and
soyuka
as code owners
November 5, 2026 12:41
dg
added a commit
to dg/pohoda-mcp
that referenced
this pull request
Nov 5, 2026
PHP on Windows waits for data on a pipe in a loop of 3,200,000 usleep(10) calls, also in non-blocking mode. One call takes about 15 ms there, so fgets() on an idle pipe blocks for about 13.5 hours and then reports a false EOF, which ends the server with exit code 0 and no error. mcp/sdk calls fgets() on an idle pipe, so the transport now reads only after stream_select() reports input. It needs PHP 8.5, as stream_select() on Windows reports an idle pipe as readable before that. See php/php-src#25204 and modelcontextprotocol/php-sdk#671.
dg
added a commit
to dg/pohoda-mcp
that referenced
this pull request
Nov 5, 2026
PHP on Windows waits for data on a pipe in a loop of 3,200,000 usleep(10) calls, also in non-blocking mode. One call takes about 15 ms there, so fgets() on an idle pipe blocks for about 13.5 hours and then reports a false EOF, which ends the server with exit code 0 and no error. mcp/sdk calls fgets() on an idle pipe, so the transport now reads only after stream_select() reports input. It needs PHP 8.5, as stream_select() on Windows reports an idle pipe as readable before that. See php/php-src#25204 and modelcontextprotocol/php-sdk#671.
dg
added a commit
to dg/pohoda-mcp
that referenced
this pull request
Nov 5, 2026
PHP on Windows waits for data on a pipe in a loop of 3,200,000 usleep(10) calls, also in non-blocking mode. One call takes about 15 ms there, so fgets() on an idle pipe blocks for about 13.5 hours and then reports a false EOF, which ends the server with exit code 0 and no error. mcp/sdk calls fgets() on an idle pipe, so the transport now reads only after stream_select() reports input. It needs PHP 8.5, as stream_select() on Windows reports an idle pipe as readable before that. See php/php-src#25204 and modelcontextprotocol/php-sdk#671.
dg
added a commit
to dg/pohoda-mcp
that referenced
this pull request
Nov 5, 2026
PHP on Windows waits for data on a pipe in a loop of 3,200,000 usleep(10) calls, also in non-blocking mode. One call takes about 15 ms there, so fgets() on an idle pipe blocks for about 13.5 hours and then reports a false EOF, which ends the server with exit code 0 and no error. mcp/sdk calls fgets() on an idle pipe, so the transport now reads only after stream_select() reports input. It needs PHP 8.5, as stream_select() on Windows reports an idle pipe as readable before that. See php/php-src#25204 and modelcontextprotocol/php-sdk#671.
dg
added a commit
to dg/pohoda-mcp
that referenced
this pull request
Nov 5, 2026
PHP on Windows waits for data on a pipe in a loop of 3,200,000 usleep(10) calls, also in non-blocking mode. One call takes about 15 ms there, so fgets() on an idle pipe blocks for about 13.5 hours and then reports a false EOF, which ends the server with exit code 0 and no error. mcp/sdk calls fgets() on an idle pipe, so the transport now reads only after stream_select() reports input. It needs PHP 8.5, as stream_select() on Windows reports an idle pipe as readable before that. The run loop then also runs on an idle connection, and mcp/sdk 0.9 only reads the session there, so the first read past its TTL of an hour deletes it and every later request gets "Session not found or has expired" without an id. A stdio session lasts as long as the process, so the server gives it no TTL. See php/php-src#25204, modelcontextprotocol/php-sdk#671 and modelcontextprotocol/php-sdk#672.
dg
added a commit
to dg/pohoda-mcp
that referenced
this pull request
Nov 5, 2026
PHP on Windows waits for data on a pipe in a loop of 3,200,000 usleep(10) calls, also in non-blocking mode. One call takes about 15 ms there, so fgets() on an idle pipe blocks for about 13.5 hours and then reports a false EOF, which ends the server with exit code 0 and no error. mcp/sdk calls fgets() on an idle pipe, so the transport now reads only after stream_select() reports input. It needs PHP 8.5, as stream_select() on Windows reports an idle pipe as readable before that. The run loop then also runs on an idle connection, and mcp/sdk 0.9 only reads the session there, so the first read past its TTL of an hour deletes it and every later request gets "Session not found or has expired" without an id. A stdio session lasts as long as the process, so the server gives it no TTL. See php/php-src#25204, modelcontextprotocol/php-sdk#671 and modelcontextprotocol/php-sdk#672.
dg
added a commit
to nette/mcp-inspector
that referenced
this pull request
Nov 6, 2026
PHP on Windows waits for data on a pipe in a loop of 3,200,000 usleep(10) calls, also in non-blocking mode. One call takes about 15 ms there, so fgets() on an idle pipe blocks for about 13.5 hours and then reports a false EOF, which ends the server with exit code 0 and no error. mcp/sdk calls fgets() on an idle pipe, so the transport now reads only after stream_select() reports input. It needs PHP 8.5, as stream_select() on Windows reports an idle pipe as readable before that. The run loop then also runs on an idle connection, and mcp/sdk 0.9 only reads the session there, so the first read past its TTL of an hour deletes it and every later request gets "Session not found or has expired" without an id. A stdio session lasts as long as the process, so the server gives it no TTL. See php/php-src#25204, modelcontextprotocol/php-sdk#671 and modelcontextprotocol/php-sdk#672.
dg
added a commit
to dg/google-services
that referenced
this pull request
Nov 6, 2026
PHP on Windows waits for data on a pipe in a loop of 3,200,000 usleep(10) calls, also in non-blocking mode. One call takes about 15 ms there, so fgets() on an idle pipe blocks for about 13.5 hours and then reports a false EOF, which ends the server with exit code 0 and no error. mcp/sdk calls fgets() on an idle pipe, so the transport now reads only after stream_select() reports input. It needs PHP 8.5, as stream_select() on Windows reports an idle pipe as readable before that. The run loop then also runs on an idle connection, and mcp/sdk 0.9 only reads the session there, so the first read past its TTL of an hour deletes it and every later request gets "Session not found or has expired" without an id. A stdio session lasts as long as the process, so the server gives it no TTL. See php/php-src#25204, modelcontextprotocol/php-sdk#671 and modelcontextprotocol/php-sdk#672.
dg
added a commit
to dg/fio-mcp
that referenced
this pull request
Nov 6, 2026
PHP on Windows waits for data on a pipe in a loop of 3,200,000 usleep(10) calls, also in non-blocking mode. One call takes about 15 ms there, so fgets() on an idle pipe blocks for about 13.5 hours and then reports a false EOF, which ends the server with exit code 0 and no error. mcp/sdk calls fgets() on an idle pipe, so the transport now reads only after stream_select() reports input. It needs PHP 8.5, as stream_select() on Windows reports an idle pipe as readable before that. The run loop then also runs on an idle connection, and mcp/sdk 0.9 only reads the session there, so the first read past its TTL of an hour deletes it and every later request gets "Session not found or has expired" without an id. A stdio session lasts as long as the process, so the server gives it no TTL. See php/php-src#25204, modelcontextprotocol/php-sdk#671 and modelcontextprotocol/php-sdk#672.
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
On Windows,
fgets()on an idle pipe blocks even in non-blocking mode and after about 13.5 hours reports a false EOF, so a stdio server ends by itself (php/php-src#25204).StdioTransportnow reads only oncestream_select()reports input, which also keeps the run loop going while idle. This needs PHP 8.5, older versions behave as before.