Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
41 changes: 13 additions & 28 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 @@ -187,7 +181,7 @@ use Mcp\Server\Transport\StreamableHttpTransport;
class McpController
{
#[Route('/mcp', name: 'mcp_endpoint']
public function handle(Request $request, Server $mcpServer): Response
public function handle(Request $request, Server $server): Response
{
// Create PSR-7 factories
$psr17Factory = new Psr17Factory();
Expand All @@ -199,8 +193,7 @@ class McpController

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

// Convert PSR-7 response back to Symfony
return $httpFoundationFactory->createResponse($psrResponse);
Expand Down Expand Up @@ -230,17 +223,16 @@ use Nyholm\Psr7\Factory\Psr17Factory;

class McpController
{
public function handle(ServerRequestInterface $request, Server $mcpServer): ResponseInterface
public function handle(ServerRequestInterface $request, Server $server): ResponseInterface
{
$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 $server->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) {
$server = 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 $server->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($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($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($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($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($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($result);
15 changes: 14 additions & 1 deletion src/Server.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,14 @@ public static function builder(): Builder
return new Builder();
}

public function connect(TransportInterface $transport): void
/**
* @template TResult
*
* @param TransportInterface<TResult> $transport
*
* @return TResult
*/
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 +63,11 @@ public function connect(TransportInterface $transport): void
$transport->onSessionEnd(function (Uuid $sessionId) {
$this->jsonRpcHandler->destroySession($sessionId);
});

try {
return $transport->listen();
} finally {
$transport->close();
}
}
}
7 changes: 7 additions & 0 deletions src/Server/Transport/InMemoryTransport.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
use Symfony\Component\Uid\Uuid;

/**
* @implements TransportInterface<null>
*
* @author Tobias Nyholm <[email protected]>
*/
class InMemoryTransport implements TransportInterface
Expand Down Expand Up @@ -50,6 +52,9 @@ public function send(string $data, array $context): void
}
}

/**
* @return null
*/
public function listen(): mixed
{
foreach ($this->messages as $message) {
Expand All @@ -60,6 +65,7 @@ public function listen(): mixed

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

return null;
Expand All @@ -74,6 +80,7 @@ public function close(): void
{
if (\is_callable($this->sessionDestroyListener) && null !== $this->sessionId) {
\call_user_func($this->sessionDestroyListener, $this->sessionId);
$this->sessionId = null;
}
}
}
13 changes: 11 additions & 2 deletions src/Server/Transport/StdioTransport.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
use Symfony\Component\Uid\Uuid;

/**
* @implements TransportInterface<int>
*
* @author Kyrian Obikwelu <[email protected]>
*/
class StdioTransport implements TransportInterface
Expand Down Expand Up @@ -59,13 +61,18 @@ public function send(string $data, array $context): void
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;
}

Expand All @@ -82,9 +89,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 +104,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
4 changes: 3 additions & 1 deletion src/Server/Transport/StreamableHttpTransport.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
use Symfony\Component\Uid\Uuid;

/**
* @implements TransportInterface<ResponseInterface>
*
* @author Kyrian Obikwelu <[email protected]>
*/
class StreamableHttpTransport implements TransportInterface
Expand Down Expand Up @@ -78,7 +80,7 @@ public function send(string $data, array $context): void
]);
}

public function listen(): mixed
public function listen(): ResponseInterface
{
return match ($this->request->getMethod()) {
'OPTIONS' => $this->handleOptionsRequest(),
Expand Down
Loading