The HTTP transport is a PSR-7 request handler, not a web server. This page covers how it fits into an application you already have.
The HTTP transport doesn't run its own web server. Instead, it processes PSR-7 requests and returns PSR-7 responses that your application can handle however it needs to:
Your Web App → PSR-7 Request → StreamableHttpTransport → PSR-7 Response → Your Web App
This design allows integration with any PHP framework or application that supports PSR-7.
Here's a simplified example using PSR-17 discovery and Laminas emitter:
use Http\Discovery\Psr17Factory;
use Mcp\Server;
use Mcp\Server\Transport\StreamableHttpTransport;
use Mcp\Server\Session\FileSessionStore;
use Laminas\HttpHandlerRunner\Emitter\SapiEmitter;
$psr17Factory = new Psr17Factory();
$request = $psr17Factory->createServerRequestFromGlobals();
$server = Server::builder()
->setServerInfo('HTTP Server', '1.0.0')
->setDiscovery(__DIR__, ['.'], excludeDirs: ['vendor'])
->setSession(new FileSessionStore(__DIR__ . '/sessions')) // HTTP needs persistent sessions
->build();
$transport = new StreamableHttpTransport($request);
$response = $server->run($transport);
(new SapiEmitter())->emit($response);First install the required PSR libraries:
composer require symfony/psr-http-message-bridge nyholm/psr7Then create a controller that uses Symfony's PSR-7 bridge:
Note: This example assumes your MCP
Serverinstance is configured in Symfony's service container.
// In a Symfony controller
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Bridge\PsrHttpMessage\Factory\PsrHttpFactory;
use Symfony\Bridge\PsrHttpMessage\Factory\HttpFoundationFactory;
use Mcp\Server;
use Mcp\Server\Transport\StreamableHttpTransport;
class McpController
{
#[Route('/mcp', name: 'mcp_endpoint')]
public function handle(Request $request, Server $server): Response
{
// Convert Symfony request to PSR-7 (PSR-17 factories auto-discovered)
$psrHttpFactory = new PsrHttpFactory();
$httpFoundationFactory = new HttpFoundationFactory();
$psrRequest = $psrHttpFactory->createRequest($request);
// Process with MCP (factories auto-discovered)
$transport = new StreamableHttpTransport($psrRequest);
$psrResponse = $server->run($transport);
// Convert PSR-7 response back to Symfony
return $httpFoundationFactory->createResponse($psrResponse);
}
}First install the required PSR libraries:
composer require symfony/psr-http-message-bridge nyholm/psr7Then create a controller that type-hints ServerRequestInterface:
Note: This example assumes your MCP
Serverinstance is constructed and bound in a Laravel service provider for dependency injection.
// In a Laravel controller
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\ResponseInterface;
use Mcp\Server;
use Mcp\Server\Transport\StreamableHttpTransport;
class McpController
{
public function handle(ServerRequestInterface $request, Server $server): ResponseInterface
{
// Create the MCP HTTP transport
$transport = new StreamableHttpTransport($request);
// Process MCP request and return PSR-7 response
// Laravel automatically handles PSR-7 responses
return $server->run($transport);
}
}
// Route registration
Route::any('/mcp', [McpController::class, 'handle']);Slim Framework works natively with PSR-7.
Create a route handler using Slim's built-in factories and container:
use Slim\Factory\AppFactory;
use Mcp\Server;
use Mcp\Server\Transport\StreamableHttpTransport;
$app = AppFactory::create();
$app->any('/mcp', function ($request, $response) {
$server = Server::builder()
->setServerInfo('My MCP Server', '1.0.0')
->setDiscovery(__DIR__, ['.'], excludeDirs: ['vendor'])
->build();
$transport = new StreamableHttpTransport($request);
return $server->run($transport);
});The transport handles all HTTP methods automatically:
- POST: Send MCP requests
- GET: Not implemented (returns 405)
- DELETE: End session
- OPTIONS: CORS preflight
You should route all methods to your MCP endpoint, not just POST.
HTTP transport requires persistent sessions since PHP doesn't maintain state between requests. Unlike STDIO transport where in-memory sessions work fine, HTTP transport needs a persistent session store:
use Mcp\Server\Session\FileSessionStore;
// ✅ Good for HTTP
$server = Server::builder()
->setSession(new FileSessionStore(__DIR__ . '/sessions'))
->build();
// ❌ Not recommended for HTTP (sessions lost between requests)
$server = Server::builder()
->setSession(new InMemorySessionStore())
->build();It's recommended to mount the MCP endpoint at /mcp, but this is not enforced:
// Recommended
Route::any('/mcp', [McpController::class, 'handle']);
// Also valid
Route::any('/', [McpController::class, 'handle']);
Route::any('/api/mcp', [McpController::class, 'handle']);Use the MCP Inspector to test HTTP servers:
# Start your PHP server
php -S localhost:8000 server.php
# Connect with MCP Inspector
npx @modelcontextprotocol/inspector http://localhost:8000