@@ -95,24 +95,47 @@ and process requests and send responses. It provides a flexible architecture tha
9595
9696``` php
9797use Psr\Http\Message\ServerRequestInterface;
98- use Psr\Http\Message\ResponseFactoryInterface;
99- use Psr\Http\Message\StreamFactoryInterface;
10098
99+ // PSR-17 factories are automatically discovered
101100$transport = new StreamableHttpTransport(
102- request: $serverRequest, // PSR-7 server request
103- responseFactory: $responseFactory, // PSR-17 response factory
104- streamFactory: $streamFactory , // PSR-17 stream factory
105- logger: $logger // Optional PSR-3 logger
101+ request: $serverRequest, // PSR-7 server request
102+ responseFactory: null, // Optional: PSR-17 response factory (auto-discovered if null)
103+ streamFactory: null , // Optional: PSR-17 stream factory (auto-discovered if null)
104+ logger: $logger // Optional PSR-3 logger
106105);
107106```
108107
109108### Parameters
110109
111110- ** ` request ` ** (required): ` ServerRequestInterface ` - The incoming PSR-7 HTTP request
112- - ** ` responseFactory ` ** (required ): ` ResponseFactoryInterface ` - PSR-17 factory for creating HTTP responses
113- - ** ` streamFactory ` ** (required ): ` StreamFactoryInterface ` - PSR-17 factory for creating response body streams
111+ - ** ` responseFactory ` ** (optional ): ` ResponseFactoryInterface ` - PSR-17 factory for creating HTTP responses. Auto-discovered if not provided.
112+ - ** ` streamFactory ` ** (optional ): ` StreamFactoryInterface ` - PSR-17 factory for creating response body streams. Auto-discovered if not provided.
114113- ** ` logger ` ** (optional): ` LoggerInterface ` - PSR-3 logger for debugging. Defaults to ` NullLogger ` .
115114
115+ ### PSR-17 Auto-Discovery
116+
117+ The transport automatically discovers PSR-17 factory implementations from these popular packages:
118+
119+ - ` nyholm/psr7 `
120+ - ` guzzlehttp/psr7 `
121+ - ` slim/psr7 `
122+ - ` laminas/laminas-diactoros `
123+ - And other PSR-17 compatible implementations
124+
125+ ``` bash
126+ # Install any PSR-17 package - discovery works automatically
127+ composer require nyholm/psr7
128+ ```
129+
130+ If auto-discovery fails or you want to use a specific implementation, you can pass factories explicitly:
131+
132+ ``` php
133+ use Nyholm\Psr7\Factory\Psr17Factory;
134+
135+ $psr17Factory = new Psr17Factory();
136+ $transport = new StreamableHttpTransport($request, $psr17Factory, $psr17Factory);
137+ ```
138+
116139### Architecture
117140
118141The HTTP transport doesn't run its own web server. Instead, it processes PSR-7 requests and returns PSR-7 responses that
@@ -126,27 +149,25 @@ This design allows integration with any PHP framework or application that suppor
126149
127150### Basic Usage (Standalone)
128151
129- Here's an opinionated example using Nyholm PSR-7 and Laminas emitter:
152+ Here's a simplified example using PSR-17 discovery and Laminas emitter:
130153
131154``` php
155+ use Http\Discovery\Psr17Factory;
132156use Mcp\Server;
133157use Mcp\Server\Transport\StreamableHttpTransport;
134158use Mcp\Server\Session\FileSessionStore;
135- use Nyholm\Psr7\Factory\Psr17Factory;
136- use Nyholm\Psr7Server\ServerRequestCreator;
137159use Laminas\HttpHandlerRunner\Emitter\SapiEmitter;
138160
139161$psr17Factory = new Psr17Factory();
140- $creator = new ServerRequestCreator($psr17Factory, $psr17Factory, $psr17Factory, $psr17Factory);
141- $request = $creator->fromGlobals();
162+ $request = $psr17Factory->createServerRequestFromGlobals();
142163
143164$server = Server::builder()
144165 ->setServerInfo('HTTP Server', '1.0.0')
145166 ->setDiscovery(__DIR__, ['.'])
146167 ->setSession(new FileSessionStore(__DIR__ . '/sessions')) // HTTP needs persistent sessions
147168 ->build();
148169
149- $transport = new StreamableHttpTransport($request, $psr17Factory, $psr17Factory );
170+ $transport = new StreamableHttpTransport($request);
150171
151172$response = $server->run($transport);
152173
@@ -174,27 +195,23 @@ use Symfony\Component\HttpFoundation\Response;
174195use Symfony\Component\Routing\Attribute\Route;
175196use Symfony\Bridge\PsrHttpMessage\Factory\PsrHttpFactory;
176197use Symfony\Bridge\PsrHttpMessage\Factory\HttpFoundationFactory;
177- use Nyholm\Psr7\Factory\Psr17Factory;
178198use Mcp\Server;
179199use Mcp\Server\Transport\StreamableHttpTransport;
180200
181201class McpController
182202{
183- #[Route('/mcp', name: 'mcp_endpoint']
203+ #[Route('/mcp', name: 'mcp_endpoint') ]
184204 public function handle(Request $request, Server $server): Response
185205 {
186- // Create PSR-7 factories
187- $psr17Factory = new Psr17Factory();
188- $psrHttpFactory = new PsrHttpFactory($psr17Factory, $psr17Factory, $psr17Factory, $psr17Factory);
206+ // Convert Symfony request to PSR-7 (PSR-17 factories auto-discovered)
207+ $psrHttpFactory = new PsrHttpFactory();
189208 $httpFoundationFactory = new HttpFoundationFactory();
190-
191- // Convert Symfony request to PSR-7
192209 $psrRequest = $psrHttpFactory->createRequest($request);
193-
194- // Process with MCP
195- $transport = new StreamableHttpTransport($psrRequest, $psr17Factory, $psr17Factory );
210+
211+ // Process with MCP (factories auto-discovered)
212+ $transport = new StreamableHttpTransport($psrRequest);
196213 $psrResponse = $server->run($transport);
197-
214+
198215 // Convert PSR-7 response back to Symfony
199216 return $httpFoundationFactory->createResponse($psrResponse);
200217 }
@@ -219,17 +236,14 @@ use Psr\Http\Message\ServerRequestInterface;
219236use Psr\Http\Message\ResponseInterface;
220237use Mcp\Server;
221238use Mcp\Server\Transport\StreamableHttpTransport;
222- use Nyholm\Psr7\Factory\Psr17Factory;
223239
224240class McpController
225241{
226242 public function handle(ServerRequestInterface $request, Server $server): ResponseInterface
227243 {
228- $psr17Factory = new Psr17Factory();
229-
230244 // Create the MCP HTTP transport
231- $transport = new StreamableHttpTransport($request, $psr17Factory, $psr17Factory );
232-
245+ $transport = new StreamableHttpTransport($request);
246+
233247 // Process MCP request and return PSR-7 response
234248 // Laravel automatically handles PSR-7 responses
235249 return $server->run($transport);
@@ -248,8 +262,6 @@ Create a route handler using Slim's built-in factories and container:
248262
249263``` php
250264use Slim\Factory\AppFactory;
251- use Slim\Psr7\Factory\ResponseFactory;
252- use Slim\Psr7\Factory\StreamFactory;
253265use Mcp\Server;
254266use Mcp\Server\Transport\StreamableHttpTransport;
255267
@@ -260,12 +272,9 @@ $app->any('/mcp', function ($request, $response) {
260272 ->setServerInfo('My MCP Server', '1.0.0')
261273 ->setDiscovery(__DIR__, ['.'])
262274 ->build();
263-
264- $responseFactory = new ResponseFactory();
265- $streamFactory = new StreamFactory();
266-
267- $transport = new StreamableHttpTransport($request, $responseFactory, $streamFactory);
268-
275+
276+ $transport = new StreamableHttpTransport($request);
277+
269278 return $server->run($transport);
270279});
271280```
@@ -330,6 +339,3 @@ npx @modelcontextprotocol/inspector http://localhost:8000
330339The choice between STDIO and HTTP transport depends on the client you want to integrate with.
331340If you are integrating with a client that is running ** locally** (like Claude Desktop), use STDIO.
332341If you are building a server in a distributed environment and need to integrate with a ** remote** client, use Streamable HTTP.
333-
334- One additiona difference to consider is that STDIO is process-based (one session per process) while HTTP is
335- request-based (multiple sessions via headers).
0 commit comments