Skip to content

Commit 719262b

Browse files
feat(server): Rework transport architecture and add StreamableHttpTransport (#49)
* feat(server): Rework transport architecture and add StreamableHttpTransport - `Server::connect()` no longer contains a processing loop. - `TransportInterface` is updated with `setMessageHandler()` and `listen()`. - `StdioTransport` is updated to implement the new interface. - A new, minimal `StreamableHttpTransport` is added for stateless HTTP. * refactor(server): use event-driven message handling * feat(server): Introduce a formal session management system * fix: ensure session elements are preserved when building server (regression from #46) * refactor: consolidate HTTP example to use shared dependencies * feat(server): Enhance message handling with session support - Updated `TransportInterface` to use `onMessage` for handling incoming messages with session IDs. - Refactored `Server`, `Handler`, and transport classes to accommodate session management using `Uuid`. - Introduced methods for creating sessions with auto-generated and specific UUIDs in `SessionFactory` and `SessionFactoryInterface`. * feat(server): Integrate session management in message handling - Added session support to the `Server` and `Handler` classes, allowing for session data to be managed during message processing. - Updated `TransportInterface` to include session context in the `send` method. - Refactored various request handlers to utilize session information, ensuring proper session handling for incoming requests. - Introduced a file-based session store for persistent session data management * feat: Update session builder method to use set* instead of with* * feat: Fix test compatibility with new session management architecture * chore: apply code style fixes * fix: remove deprecated SSE transport and resolve PHPStan issues
1 parent b1e54f1 commit 719262b

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+1614
-406
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@
33
composer.lock
44
vendor
55
examples/**/dev.log
6+
examples/**/sessions

composer.json

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,11 @@
2222
"ext-fileinfo": "*",
2323
"opis/json-schema": "^2.4",
2424
"phpdocumentor/reflection-docblock": "^5.6",
25+
"psr/clock": "^1.0",
2526
"psr/container": "^2.0",
2627
"psr/event-dispatcher": "^1.0",
28+
"psr/http-factory": "^1.1",
29+
"psr/http-message": "^2.0",
2730
"psr/log": "^1.0 || ^2.0 || ^3.0",
2831
"symfony/finder": "^6.4 || ^7.3",
2932
"symfony/uid": "^6.4 || ^7.3"
@@ -34,7 +37,10 @@
3437
"phpunit/phpunit": "^10.5",
3538
"psr/cache": "^3.0",
3639
"symfony/console": "^6.4 || ^7.3",
37-
"symfony/process": "^6.4 || ^7.3"
40+
"symfony/process": "^6.4 || ^7.3",
41+
"nyholm/psr7": "^1.8",
42+
"nyholm/psr7-server": "^1.1",
43+
"laminas/laminas-httphandlerrunner": "^2.12"
3844
},
3945
"autoload": {
4046
"psr-4": {
@@ -51,10 +57,11 @@
5157
"Mcp\\Example\\DependenciesStdioExample\\": "examples/06-custom-dependencies-stdio/",
5258
"Mcp\\Example\\ComplexSchemaHttpExample\\": "examples/07-complex-tool-schema-http/",
5359
"Mcp\\Example\\SchemaShowcaseExample\\": "examples/08-schema-showcase-streamable/",
60+
"Mcp\\Example\\HttpTransportExample\\": "examples/10-simple-http-transport/",
5461
"Mcp\\Tests\\": "tests/"
5562
}
5663
},
5764
"config": {
5865
"sort-packages": true
5966
}
60-
}
67+
}

examples/01-discovery-stdio-calculator/server.php

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,17 @@
1818

1919
logger()->info('Starting MCP Stdio Calculator Server...');
2020

21-
Server::make()
21+
$server = Server::make()
2222
->setServerInfo('Stdio Calculator', '1.1.0', 'Basic Calculator over STDIO transport.')
2323
->setContainer(container())
2424
->setLogger(logger())
2525
->setDiscovery(__DIR__, ['.'])
26-
->build()
27-
->connect(new StdioTransport(logger: logger()));
26+
->build();
27+
28+
$transport = new StdioTransport(logger: logger());
29+
30+
$server->connect($transport);
31+
32+
$transport->listen();
2833

2934
logger()->info('Server listener stopped gracefully.');

examples/02-discovery-http-userprofile/server.php

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,20 +13,23 @@
1313
require_once dirname(__DIR__).'/bootstrap.php';
1414
chdir(__DIR__);
1515

16-
use Mcp\Capability\Registry\Container;
16+
use Laminas\HttpHandlerRunner\Emitter\SapiEmitter;
1717
use Mcp\Server;
18-
use Psr\Log\LoggerInterface;
18+
use Mcp\Server\Session\FileSessionStore;
19+
use Mcp\Server\Transport\StreamableHttpTransport;
20+
use Nyholm\Psr7\Factory\Psr17Factory;
21+
use Nyholm\Psr7Server\ServerRequestCreator;
1922

20-
logger()->info('Starting MCP HTTP User Profile Server...');
23+
$psr17Factory = new Psr17Factory();
24+
$creator = new ServerRequestCreator($psr17Factory, $psr17Factory, $psr17Factory, $psr17Factory);
2125

22-
// --- Setup DI Container for DI in McpElements class ---
23-
$container = new Container();
24-
$container->set(LoggerInterface::class, logger());
26+
$request = $creator->fromGlobals();
2527

26-
Server::make()
28+
$server = Server::make()
2729
->setServerInfo('HTTP User Profiles', '1.0.0')
2830
->setLogger(logger())
29-
->setContainer($container)
31+
->setContainer(container())
32+
->setSession(new FileSessionStore(__DIR__.'/sessions'))
3033
->setDiscovery(__DIR__, ['.'])
3134
->addTool(
3235
function (float $a, float $b, string $operation = 'add'): array {
@@ -70,7 +73,12 @@ function (): array {
7073
description: 'Current system status and runtime information',
7174
mimeType: 'application/json'
7275
)
73-
->build()
74-
->connect(new StreamableHttpServerTransport('127.0.0.1', 8080, 'mcp'));
76+
->build();
7577

76-
logger()->info('Server listener stopped gracefully.');
78+
$transport = new StreamableHttpTransport($request, $psr17Factory, $psr17Factory);
79+
80+
$server->connect($transport);
81+
82+
$response = $transport->listen();
83+
84+
(new SapiEmitter())->emit($response);

examples/03-manual-registration-stdio/server.php

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,20 @@
1919

2020
logger()->info('Starting MCP Manual Registration (Stdio) Server...');
2121

22-
Server::make()
22+
$server = Server::make()
2323
->setServerInfo('Manual Reg Server', '1.0.0')
2424
->setLogger(logger())
2525
->setContainer(container())
2626
->addTool([SimpleHandlers::class, 'echoText'], 'echo_text')
2727
->addResource([SimpleHandlers::class, 'getAppVersion'], 'app://version', 'application_version', mimeType: 'text/plain')
2828
->addPrompt([SimpleHandlers::class, 'greetingPrompt'], 'personalized_greeting')
2929
->addResourceTemplate([SimpleHandlers::class, 'getItemDetails'], 'item://{itemId}/details', 'get_item_details', mimeType: 'application/json')
30-
->build()
31-
->connect(new StdioTransport(logger: logger()));
30+
->build();
31+
32+
$transport = new StdioTransport(logger: logger());
33+
34+
$server->connect($transport);
35+
36+
$transport->listen();
3237

3338
logger()->info('Server listener stopped gracefully.');

examples/04-combined-registration-http/server.php

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,24 +13,37 @@
1313
require_once dirname(__DIR__).'/bootstrap.php';
1414
chdir(__DIR__);
1515

16+
use Laminas\HttpHandlerRunner\Emitter\SapiEmitter;
1617
use Mcp\CombinedHttpExample\Manual\ManualHandlers;
1718
use Mcp\Server;
18-
use Mcp\Server\Transports\HttpServerTransport;
19+
use Mcp\Server\Session\FileSessionStore;
20+
use Mcp\Server\Transport\StreamableHttpTransport;
21+
use Nyholm\Psr7\Factory\Psr17Factory;
22+
use Nyholm\Psr7Server\ServerRequestCreator;
1923

20-
logger()->info('Starting MCP Combined Registration (HTTP) Server...');
24+
$psr17Factory = new Psr17Factory();
25+
$creator = new ServerRequestCreator($psr17Factory, $psr17Factory, $psr17Factory, $psr17Factory);
2126

22-
Server::make()
27+
$request = $creator->fromGlobals();
28+
29+
$server = Server::make()
2330
->setServerInfo('Combined HTTP Server', '1.0.0')
2431
->setLogger(logger())
2532
->setContainer(container())
33+
->setSession(new FileSessionStore(__DIR__.'/sessions'))
2634
->setDiscovery(__DIR__, ['.'])
2735
->addTool([ManualHandlers::class, 'manualGreeter'])
2836
->addResource(
2937
[ManualHandlers::class, 'getPriorityConfigManual'],
3038
'config://priority',
3139
'priority_config_manual',
3240
)
33-
->build()
34-
->connect(new HttpServerTransport('127.0.0.1', 8081, 'mcp_combined'));
41+
->build();
42+
43+
$transport = new StreamableHttpTransport($request, $psr17Factory, $psr17Factory);
44+
45+
$server->connect($transport);
46+
47+
$response = $transport->listen();
3548

36-
logger()->info('Server listener stopped gracefully.');
49+
(new SapiEmitter())->emit($response);

examples/05-stdio-env-variables/server.php

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,11 +49,16 @@
4949

5050
logger()->info('Starting MCP Stdio Environment Variable Example Server...');
5151

52-
Server::make()
52+
$server = Server::make()
5353
->setServerInfo('Env Var Server', '1.0.0')
5454
->setLogger(logger())
5555
->setDiscovery(__DIR__, ['.'])
56-
->build()
57-
->connect(new StdioTransport(logger: logger()));
56+
->build();
57+
58+
$transport = new StdioTransport(logger: logger());
59+
60+
$server->connect($transport);
61+
62+
$transport->listen();
5863

5964
logger()->info('Server listener stopped gracefully.');

examples/06-custom-dependencies-stdio/server.php

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,17 @@
2727
$statsService = new Services\SystemStatsService($taskRepo);
2828
$container->set(Services\StatsServiceInterface::class, $statsService);
2929

30-
Server::make()
30+
$server = Server::make()
3131
->setServerInfo('Task Manager Server', '1.0.0')
3232
->setLogger(logger())
3333
->setContainer($container)
3434
->setDiscovery(__DIR__, ['.'])
35-
->build()
36-
->connect(new StdioTransport(logger: logger()));
35+
->build();
36+
37+
$transport = new StdioTransport(logger: logger());
38+
39+
$server->connect($transport);
40+
41+
$transport->listen();
3742

3843
logger()->info('Server listener stopped gracefully.');

examples/07-complex-tool-schema-http/server.php

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,30 @@
1313
require_once dirname(__DIR__).'/bootstrap.php';
1414
chdir(__DIR__);
1515

16+
use Laminas\HttpHandlerRunner\Emitter\SapiEmitter;
1617
use Mcp\Server;
17-
use Mcp\Server\Transports\HttpServerTransport;
18+
use Mcp\Server\Session\FileSessionStore;
19+
use Mcp\Server\Transport\StreamableHttpTransport;
20+
use Nyholm\Psr7\Factory\Psr17Factory;
21+
use Nyholm\Psr7Server\ServerRequestCreator;
1822

19-
logger()->info('Starting MCP Complex Schema HTTP Server...');
23+
$psr17Factory = new Psr17Factory();
24+
$creator = new ServerRequestCreator($psr17Factory, $psr17Factory, $psr17Factory, $psr17Factory);
2025

21-
Server::make()
26+
$request = $creator->fromGlobals();
27+
28+
$server = Server::make()
2229
->setServerInfo('Event Scheduler Server', '1.0.0')
2330
->setLogger(logger())
2431
->setContainer(container())
32+
->setSession(new FileSessionStore(__DIR__.'/sessions'))
2533
->setDiscovery(__DIR__, ['.'])
26-
->build()
27-
->connect(new HttpServerTransport('127.0.0.1', 8082, 'mcp_scheduler'));
34+
->build();
35+
36+
$transport = new StreamableHttpTransport($request, $psr17Factory, $psr17Factory);
37+
38+
$server->connect($transport);
39+
40+
$response = $transport->listen();
2841

29-
logger()->info('Server listener stopped gracefully.');
42+
(new SapiEmitter())->emit($response);

examples/08-schema-showcase-streamable/server.php

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,29 @@
1313
require_once dirname(__DIR__).'/bootstrap.php';
1414
chdir(__DIR__);
1515

16+
use Laminas\HttpHandlerRunner\Emitter\SapiEmitter;
1617
use Mcp\Server;
17-
use Mcp\Server\Transports\StreamableHttpServerTransport;
18+
use Mcp\Server\Session\FileSessionStore;
19+
use Mcp\Server\Transport\StreamableHttpTransport;
20+
use Nyholm\Psr7\Factory\Psr17Factory;
21+
use Nyholm\Psr7Server\ServerRequestCreator;
1822

19-
logger()->info('Starting MCP Schema Showcase Server...');
23+
$psr17Factory = new Psr17Factory();
24+
$creator = new ServerRequestCreator($psr17Factory, $psr17Factory, $psr17Factory, $psr17Factory);
2025

21-
Server::make()
26+
$request = $creator->fromGlobals();
27+
28+
$server = Server::make()
2229
->setServerInfo('Schema Showcase', '1.0.0')
2330
->setLogger(logger())
31+
->setSession(new FileSessionStore(__DIR__.'/sessions'))
2432
->setDiscovery(__DIR__, ['.'])
25-
->build()
26-
->connect(new StreamableHttpServerTransport('127.0.0.1', 8080, 'mcp'));
33+
->build();
34+
35+
$transport = new StreamableHttpTransport($request, $psr17Factory, $psr17Factory);
36+
37+
$server->connect($transport);
38+
39+
$response = $transport->listen();
2740

28-
logger()->info('Server listener stopped gracefully.');
41+
(new SapiEmitter())->emit($response);

0 commit comments

Comments
 (0)