Add a way to stop the stdioTransport in a graceful way (#195)

* Add a way to stop the stdioTransport in a graceful way

* fix cs

* fix: minors

* move around files

* add changelog
This commit is contained in:
Tobias Nyholm 2025-12-27 14:21:55 +01:00 • committed by GitHub
commit 7a8045a5ef
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 91 additions and 3 deletions

View file

@ -2,6 +2,11 @@
All notable changes to `mcp/sdk` will be documented in this file.
0.2.1
-----
* Add `RunnerControl` for `StdioTransport` to allow break out from continuously listening for new input.
0.2.0
-----
@ -13,7 +18,6 @@ All notable changes to `mcp/sdk` will be documented in this file.
* Removed `Protocol::getTransport()`
* Added parameter for `TransportInterface` to `Protocol::processInput()`
0.1.0
-----

View file

@ -0,0 +1,28 @@
<?php
/*
* This file is part of the official PHP MCP SDK.
*
* A collaboration between Symfony and the PHP Foundation.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Mcp\Server\Transport\Stdio;
/**
* Default implementation of RunnerControlInterface. This will allow
* anyone to change the state of the runner.
*
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
*/
class RunnerControl implements RunnerControlInterface
{
public static RunnerState $state = RunnerState::RUNNING;
public function getState(): RunnerState
{
return self::$state;
}
}

View file

@ -0,0 +1,22 @@
<?php
/*
* This file is part of the official PHP MCP SDK.
*
* A collaboration between Symfony and the PHP Foundation.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Mcp\Server\Transport\Stdio;
/**
* Used by the transport to control the runner state.
*
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
*/
interface RunnerControlInterface
{
public function getState(): RunnerState;
}

View file

@ -0,0 +1,24 @@
<?php
/*
* This file is part of the official PHP MCP SDK.
*
* A collaboration between Symfony and the PHP Foundation.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Mcp\Server\Transport\Stdio;
/**
* State for the transport.
*
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
*/
enum RunnerState
{
case RUNNING;
case STOP_AND_END_SESSION;
case STOP;
}

View file

@ -12,6 +12,9 @@
namespace Mcp\Server\Transport;
use Mcp\Schema\JsonRpc\Error;
use Mcp\Server\Transport\Stdio\RunnerControl;
use Mcp\Server\Transport\Stdio\RunnerControlInterface;
use Mcp\Server\Transport\Stdio\RunnerState;
use Psr\Log\LoggerInterface;
/**
@ -21,6 +24,8 @@ use Psr\Log\LoggerInterface;
*/
class StdioTransport extends BaseTransport
{
private RunnerControlInterface $runnerControl;
/**
* @param resource $input
* @param resource $output
@ -29,7 +34,9 @@ class StdioTransport extends BaseTransport
private $input = \STDIN,
private $output = \STDOUT,
?LoggerInterface $logger = null,
?RunnerControlInterface $runnerControl = null,
) {
$this->runnerControl = $runnerControl ?? new RunnerControl();
parent::__construct($logger);
}
@ -47,14 +54,17 @@ class StdioTransport extends BaseTransport
$this->logger->info('StdioTransport is listening for messages on STDIN...');
stream_set_blocking($this->input, false);
while (!feof($this->input)) {
while (!feof($this->input) && RunnerState::RUNNING === $this->runnerControl->getState()) {
$this->processInput();
$this->processFiber();
$this->flushOutgoingMessages();
}
$this->logger->info('StdioTransport finished listening.');
$this->handleSessionEnd($this->sessionId);
if (\in_array($this->runnerControl->getState(), [RunnerState::RUNNING, RunnerState::STOP_AND_END_SESSION], true)) {
$this->logger->info('StdioTransport end session.');
$this->handleSessionEnd($this->sessionId);
}
return 0;
}