[Server] Simplify server entrypoint with run-based transport lifecycle (#103)
* feat: replace connect/listen flow with run lifecycle * feat(transport): add phpstan generics for typed transport returns * refactor: standardize variable naming for MCP server instances in transport handling * test: remove unnecessary phpstan-ignore annotation in DocBlockTestFixture
This commit is contained in:
parent
e2243bc2d9
commit
2dbd411194
4 changed files with 82 additions and 74 deletions
|
|
@ -14,6 +14,8 @@ namespace Mcp\Server\Transport;
|
|||
use Symfony\Component\Uid\Uuid;
|
||||
|
||||
/**
|
||||
* @implements TransportInterface<null>
|
||||
*
|
||||
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
|
||||
*/
|
||||
class InMemoryTransport implements TransportInterface
|
||||
|
|
@ -50,6 +52,9 @@ class InMemoryTransport implements TransportInterface
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return null
|
||||
*/
|
||||
public function listen(): mixed
|
||||
{
|
||||
foreach ($this->messages as $message) {
|
||||
|
|
@ -60,6 +65,7 @@ class InMemoryTransport implements TransportInterface
|
|||
|
||||
if (\is_callable($this->sessionDestroyListener) && null !== $this->sessionId) {
|
||||
\call_user_func($this->sessionDestroyListener, $this->sessionId);
|
||||
$this->sessionId = null;
|
||||
}
|
||||
|
||||
return null;
|
||||
|
|
@ -74,6 +80,7 @@ class InMemoryTransport implements TransportInterface
|
|||
{
|
||||
if (\is_callable($this->sessionDestroyListener) && null !== $this->sessionId) {
|
||||
\call_user_func($this->sessionDestroyListener, $this->sessionId);
|
||||
$this->sessionId = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,8 @@ use Psr\Log\NullLogger;
|
|||
use Symfony\Component\Uid\Uuid;
|
||||
|
||||
/**
|
||||
* @implements TransportInterface<int>
|
||||
*
|
||||
* @author Kyrian Obikwelu <koshnawaza@gmail.com>
|
||||
*/
|
||||
class StdioTransport implements TransportInterface
|
||||
|
|
@ -59,13 +61,18 @@ class StdioTransport implements TransportInterface
|
|||
fwrite($this->output, $data.\PHP_EOL);
|
||||
}
|
||||
|
||||
public function listen(): mixed
|
||||
public function listen(): int
|
||||
{
|
||||
$this->logger->info('StdioTransport is listening for messages on STDIN...');
|
||||
|
||||
$status = 0;
|
||||
while (!feof($this->input)) {
|
||||
$line = fgets($this->input);
|
||||
if (false === $line) {
|
||||
if (!feof($this->input)) {
|
||||
$status = 1;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -82,9 +89,10 @@ class StdioTransport implements TransportInterface
|
|||
|
||||
if (\is_callable($this->sessionEndListener) && null !== $this->sessionId) {
|
||||
\call_user_func($this->sessionEndListener, $this->sessionId);
|
||||
$this->sessionId = null;
|
||||
}
|
||||
|
||||
return null;
|
||||
return $status;
|
||||
}
|
||||
|
||||
public function onSessionEnd(callable $listener): void
|
||||
|
|
@ -96,6 +104,7 @@ class StdioTransport implements TransportInterface
|
|||
{
|
||||
if (\is_callable($this->sessionEndListener) && null !== $this->sessionId) {
|
||||
\call_user_func($this->sessionEndListener, $this->sessionId);
|
||||
$this->sessionId = null;
|
||||
}
|
||||
|
||||
if (\is_resource($this->input)) {
|
||||
|
|
|
|||
|
|
@ -21,6 +21,8 @@ use Psr\Log\NullLogger;
|
|||
use Symfony\Component\Uid\Uuid;
|
||||
|
||||
/**
|
||||
* @implements TransportInterface<ResponseInterface>
|
||||
*
|
||||
* @author Kyrian Obikwelu <koshnawaza@gmail.com>
|
||||
*/
|
||||
class StreamableHttpTransport implements TransportInterface
|
||||
|
|
@ -78,7 +80,7 @@ class StreamableHttpTransport implements TransportInterface
|
|||
]);
|
||||
}
|
||||
|
||||
public function listen(): mixed
|
||||
public function listen(): ResponseInterface
|
||||
{
|
||||
return match ($this->request->getMethod()) {
|
||||
'OPTIONS' => $this->handleOptionsRequest(),
|
||||
|
|
|
|||
|
|
@ -14,6 +14,8 @@ namespace Mcp\Server\Transport;
|
|||
use Symfony\Component\Uid\Uuid;
|
||||
|
||||
/**
|
||||
* @template TResult
|
||||
*
|
||||
* @author Christopher Hertel <mail@christopher-hertel.de>
|
||||
* @author Kyrian Obikwelu <koshnawaza@gmail.com>
|
||||
*/
|
||||
|
|
@ -38,7 +40,7 @@ interface TransportInterface
|
|||
* - For a single-request transport like HTTP, this will process the request
|
||||
* and return a result (e.g., a PSR-7 Response) to be sent to the client.
|
||||
*
|
||||
* @return mixed the result of the transport's execution, if any
|
||||
* @return TResult the result of the transport's execution, if any
|
||||
*/
|
||||
public function listen(): mixed;
|
||||
|
||||
|
|
@ -63,6 +65,7 @@ interface TransportInterface
|
|||
*
|
||||
* This method should be called when the transport is no longer needed.
|
||||
* It should clean up any resources and close any connections.
|
||||
* `Server::run()` calls this automatically after `listen()` exits.
|
||||
*/
|
||||
public function close(): void;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue