Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 4 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,8 +108,8 @@ $server = Server::builder()
->build();

$transport = new StdioTransport();
$server->connect($transport);
$transport->listen();

$server->run($transport);
```

### 3. Configure Your MCP Client
Expand Down Expand Up @@ -175,15 +175,13 @@ $server = Server::builder()
**STDIO Transport** (Command-line integration):
```php
$transport = new StdioTransport();
$server->connect($transport);
$transport->listen();
$server->run($transport);
```

**HTTP Transport** (Web-based communication):
```php
$transport = new StreamableHttpTransport($request, $responseFactory, $streamFactory);
$server->connect($transport);
$response = $transport->listen();
$response = $server->run($transport);
// Handle $response in your web application
```

Expand Down
37 changes: 11 additions & 26 deletions docs/transports.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,7 @@ $server = Server::builder()

$transport = new SomeTransport();

$server->connect($transport);

$transport->listen(); // For STDIO, or handle response for HTTP
$result = $server->run($transport); // Blocks for STDIO, returns a response for HTTP
```

## STDIO Transport
Expand Down Expand Up @@ -70,9 +68,9 @@ $server = Server::builder()

$transport = new StdioTransport();

$server->connect($transport);
$status = $server->run($transport);

$transport->listen();
exit($status); // 0 on clean shutdown, non-zero if STDIN errored
```

### Client Configuration
Expand Down Expand Up @@ -138,24 +136,20 @@ use Nyholm\Psr7\Factory\Psr17Factory;
use Nyholm\Psr7Server\ServerRequestCreator;
use Laminas\HttpHandlerRunner\Emitter\SapiEmitter;

// Create PSR-7 request from globals
$psr17Factory = new Psr17Factory();
$creator = new ServerRequestCreator($psr17Factory, $psr17Factory, $psr17Factory, $psr17Factory);
$request = $creator->fromGlobals();

// Build server
$server = Server::builder()
->setServerInfo('HTTP Server', '1.0.0')
->setDiscovery(__DIR__, ['.'])
->setSession(new FileSessionStore(__DIR__ . '/sessions')) // HTTP needs persistent sessions
->build();

// Process request and get response
$transport = new StreamableHttpTransport($request, $psr17Factory, $psr17Factory);
$server->connect($transport);
$response = $transport->listen();

// Emit response
$response = $server->run($transport);

(new SapiEmitter())->emit($response);
```

Expand Down Expand Up @@ -199,8 +193,7 @@ class McpController

// Process with MCP
$transport = new StreamableHttpTransport($psrRequest, $psr17Factory, $psr17Factory);
$mcpServer->connect($transport);
$psrResponse = $transport->listen();
$psrResponse = $mcpServer->run($transport);

// Convert PSR-7 response back to Symfony
return $httpFoundationFactory->createResponse($psrResponse);
Expand Down Expand Up @@ -234,13 +227,12 @@ class McpController
{
$psr17Factory = new Psr17Factory();

// Create and connect the MCP HTTP transport
// Create the MCP HTTP transport
$transport = new StreamableHttpTransport($request, $psr17Factory, $psr17Factory);
$mcpServer->connect($transport);

// Process MCP request and return PSR-7 response
// Laravel automatically handles PSR-7 responses
return $transport->listen();
return $mcpServer->run($transport);
}
}

Expand All @@ -255,33 +247,26 @@ Slim Framework works natively with PSR-7.
Create a route handler using Slim's built-in factories and container:

```php
use Psr\Container\ContainerInterface;
use Slim\Factory\AppFactory;
use Slim\Psr7\Factory\ResponseFactory;
use Slim\Psr7\Factory\StreamFactory;
use Mcp\Server;
use Mcp\Server\Transport\StreamableHttpTransport;

$app = AppFactory::create();
$container = $app->getContainer();

$container->set('mcpServer', function (ContainerInterface $container) {
return Server::builder()
$app->any('/mcp', function ($request, $response) {
$mcpServer =Server::builder()
->setServerInfo('My MCP Server', '1.0.0')
->setDiscovery(__DIR__, ['.'])
->build();
});

$app->any('/mcp', function ($request, $response) {
$mcpServer = $this->get('mcpServer');

$responseFactory = new ResponseFactory();
$streamFactory = new StreamFactory();

$transport = new StreamableHttpTransport($request, $responseFactory, $streamFactory);
$mcpServer->connect($transport);

return $transport->listen();
return $mcpServer->run($transport);
});
```

Expand Down
6 changes: 3 additions & 3 deletions examples/custom-method-handlers/server.php
Original file line number Diff line number Diff line change
Expand Up @@ -137,8 +137,8 @@ public function handle(CallToolRequest|HasMethodInterface $message, SessionInter

$transport = new StdioTransport(logger: logger());

$server->connect($transport);
$result = $server->run($transport);

$transport->listen();
logger()->info('Server listener stopped gracefully.', ['result' => $result]);

logger()->info('Server listener stopped gracefully.');
exit((int) $result);
4 changes: 1 addition & 3 deletions examples/http-combined-registration/server.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,6 @@

$transport = new StreamableHttpTransport($request, $psr17Factory, $psr17Factory);

$server->connect($transport);

$response = $transport->listen();
$response = $server->run($transport);

(new SapiEmitter())->emit($response);
4 changes: 1 addition & 3 deletions examples/http-complex-tool-schema/server.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,6 @@

$transport = new StreamableHttpTransport($request, $psr17Factory, $psr17Factory);

$server->connect($transport);

$response = $transport->listen();
$response = $server->run($transport);

(new SapiEmitter())->emit($response);
4 changes: 1 addition & 3 deletions examples/http-discovery-userprofile/server.php
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,6 @@ function (): array {

$transport = new StreamableHttpTransport($request, $psr17Factory, $psr17Factory);

$server->connect($transport);

$response = $transport->listen();
$response = $server->run($transport);

(new SapiEmitter())->emit($response);
4 changes: 1 addition & 3 deletions examples/http-schema-showcase/server.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,6 @@

$transport = new StreamableHttpTransport($request, $psr17Factory, $psr17Factory);

$server->connect($transport);

$response = $transport->listen();
$response = $server->run($transport);

(new SapiEmitter())->emit($response);
6 changes: 3 additions & 3 deletions examples/stdio-cached-discovery/server.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@

$transport = new StdioTransport(logger: logger());

$server->connect($transport);
$result = $server->run($transport);

$transport->listen();
logger()->info('Server listener stopped gracefully.', ['result' => $result]);

logger()->info('Server listener stopped gracefully.');
exit((int) $result);
6 changes: 3 additions & 3 deletions examples/stdio-custom-dependencies/server.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@

$transport = new StdioTransport(logger: logger());

$server->connect($transport);
$result = $server->run($transport);

$transport->listen();
logger()->info('Server listener stopped gracefully.', ['result' => $result]);

logger()->info('Server listener stopped gracefully.');
exit((int) $result);
6 changes: 3 additions & 3 deletions examples/stdio-discovery-calculator/server.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@

$transport = new StdioTransport(logger: logger());

$server->connect($transport);
$result = $server->run($transport);

$transport->listen();
logger()->info('Server listener stopped gracefully.', ['result' => $result]);

logger()->info('Server listener stopped gracefully.');
exit((int) $result);
6 changes: 3 additions & 3 deletions examples/stdio-env-variables/server.php
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,8 @@

$transport = new StdioTransport(logger: logger());

$server->connect($transport);
$result = $server->run($transport);

$transport->listen();
logger()->info('Server listener stopped gracefully.', ['result' => $result]);

logger()->info('Server listener stopped gracefully.');
exit((int) $result);
6 changes: 3 additions & 3 deletions examples/stdio-explicit-registration/server.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@

$transport = new StdioTransport(logger: logger());

$server->connect($transport);
$result = $server->run($transport);

$transport->listen();
logger()->info('Server listener stopped gracefully.', ['result' => $result]);

logger()->info('Server listener stopped gracefully.');
exit((int) $result);
8 changes: 7 additions & 1 deletion src/Server.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public static function builder(): Builder
return new Builder();
}

public function connect(TransportInterface $transport): void
public function run(TransportInterface $transport): mixed
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

which scenarios do we have instead of int?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check the StreamableHttpTransport and the http examples. For that, It returns a PSR-7 response instead

{
$transport->initialize();

Expand All @@ -56,5 +56,11 @@ public function connect(TransportInterface $transport): void
$transport->onSessionEnd(function (Uuid $sessionId) {
$this->jsonRpcHandler->destroySession($sessionId);
});

try {
return $transport->listen();
} finally {
$transport->close();
}
}
}
4 changes: 3 additions & 1 deletion src/Server/Transport/InMemoryTransport.php
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,10 @@ public function listen(): mixed

if (\is_callable($this->sessionDestroyListener) && null !== $this->sessionId) {
\call_user_func($this->sessionDestroyListener, $this->sessionId);
$this->sessionId = null;
}

return null;
return 0;
}

public function onSessionEnd(callable $listener): void
Expand All @@ -74,6 +75,7 @@ public function close(): void
{
if (\is_callable($this->sessionDestroyListener) && null !== $this->sessionId) {
\call_user_func($this->sessionDestroyListener, $this->sessionId);
$this->sessionId = null;
}
}
}
9 changes: 8 additions & 1 deletion src/Server/Transport/StdioTransport.php
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,14 @@ public function listen(): mixed
{
$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;
}

Expand All @@ -82,9 +87,10 @@ public function listen(): mixed

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
Expand All @@ -96,6 +102,7 @@ public function close(): void
{
if (\is_callable($this->sessionEndListener) && null !== $this->sessionId) {
\call_user_func($this->sessionEndListener, $this->sessionId);
$this->sessionId = null;
}

if (\is_resource($this->input)) {
Expand Down
1 change: 1 addition & 0 deletions src/Server/Transport/TransportInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ public function onSessionEnd(callable $listener): void;
*
* 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;
}
7 changes: 3 additions & 4 deletions tests/Unit/ServerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,16 +32,15 @@ public function testJsonExceptions()

$transport = $this->getMockBuilder(InMemoryTransport::class)
->setConstructorArgs([['foo', 'bar']])
->onlyMethods(['send'])
->onlyMethods(['send', 'close'])
->getMock();
$transport->expects($this->exactly(2))->method('send')->willReturnOnConsecutiveCalls(
null,
null
);
$transport->expects($this->once())->method('close');

$server = new Server($handler);
$server->connect($transport);

$transport->listen();
$server->run($transport);
}
}