The STDIO transport communicates via standard input/output streams, ideal for command-line tools and MCP client integrations.
$transport = new StdioTransport(
input: STDIN, // Input stream (default: STDIN)
output: STDOUT, // Output stream (default: STDOUT)
logger: $logger // Optional PSR-3 logger
);input(optional): Input stream resource. Defaults toSTDIN.output(optional): Output stream resource. Defaults toSTDOUT.logger(optional):LoggerInterface- PSR-3 logger for debugging. Defaults toNullLogger.runnerControl(optional):RunnerControlInterface- controls the read loop. Defaults to aRunnerControl, which runs until the input stream closes.maxLineBytes(optional): Maximum accepted line length in bytes. Defaults to 4 MiB (StdioTransport::DEFAULT_MAX_LINE_BYTES). The transport discards a longer line and logs a warning. It writes nothing back to the client. A value below1throwsInvalidArgumentException.
The STDIO transport serves both protocol versions. The client's first request decides which one the connection uses, see Serving both eras.
!!! warning
When using STDIO transport, never write to STDOUT in your handlers as it's reserved for JSON-RPC communication.
Use STDERR for debugging instead.
#!/usr/bin/env php
<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use Mcp\Server;
use Mcp\Server\Transport\StdioTransport;
$server = Server::builder()
->setServerInfo('STDIO Calculator', '1.0.0')
->addTool(function(int $a, int $b): int { return $a + $b; }, 'add_numbers')
->addTool(InvokableCalculator::class)
->build();
$transport = new StdioTransport();
$status = $server->run($transport);
exit($status); // listen() returns 0 when the input stream closes
For MCP clients like Claude Desktop:
{
"mcpServers": {
"my-php-server": {
"command": "php",
"args": ["/absolute/path/to/server.php"]
}
}
}