Files
redmine/redMCP/app/McpStdioServer.php
Jason Thistlethwaite bd26c8894f Add production rollout tooling and semantic index ops docs
Capture the production plugin rollout workflow and Qdrant validation steps so operations stay repeatable. Also harden redMCP stdio/schema compatibility to keep diverse MCP clients and validators working.
2026-05-06 22:18:02 -04:00

91 lines
2.4 KiB
PHP

<?php
declare(strict_types=1);
namespace RedMCP;
final class McpStdioServer
{
private McpDispatcher $dispatcher;
private string $wireMode = 'content-length';
public function __construct(McpDispatcher $dispatcher)
{
$this->dispatcher = $dispatcher;
}
public function run(): void
{
while (($message = $this->readMessage(STDIN)) !== null) {
$response = $this->dispatcher->handleMessage($message, ['transport' => 'stdio']);
if ($response !== null) {
$this->writeMessage($response);
}
}
}
/**
* @param resource $stream
*
* @return array<string,mixed>|null
*/
private function readMessage($stream): ?array
{
$headers = [];
while (($line = fgets($stream)) !== false) {
$line = rtrim($line, "\r\n");
if ($line === '') {
break;
}
if (!preg_match('/^[A-Za-z0-9-]+\s*:/', $line)) {
$this->wireMode = 'line';
$decoded = json_decode($line, true);
return is_array($decoded) ? $decoded : null;
}
[$name, $value] = explode(':', $line, 2);
$headers[strtolower(trim($name))] = trim($value);
}
if ($line === false && $headers === []) {
return null;
}
$length = isset($headers['content-length']) ? (int) $headers['content-length'] : 0;
if ($length <= 0) {
return null;
}
$body = '';
while (strlen($body) < $length && !feof($stream)) {
$chunk = fread($stream, $length - strlen($body));
if ($chunk === false || $chunk === '') {
break;
}
$body .= $chunk;
}
$decoded = json_decode($body, true);
return is_array($decoded) ? $decoded : null;
}
/**
* @param array<string,mixed> $message
*/
private function writeMessage(array $message): void
{
$body = json_encode($message, JSON_UNESCAPED_SLASHES);
if ($body === false) {
return;
}
if ($this->wireMode === 'line') {
fwrite(STDOUT, $body . "\n");
fflush(STDOUT);
return;
}
fwrite(STDOUT, 'Content-Length: ' . strlen($body) . "\r\n\r\n" . $body);
fflush(STDOUT);
}
}