Add redMCP Streamable HTTP server
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace RedMCP;
|
||||
|
||||
final class McpStdioServer
|
||||
{
|
||||
private McpDispatcher $dispatcher;
|
||||
|
||||
public function __construct(McpDispatcher $dispatcher)
|
||||
{
|
||||
$this->dispatcher = $dispatcher;
|
||||
}
|
||||
|
||||
public function run(): void
|
||||
{
|
||||
while (($message = $this->readMessage(STDIN)) !== null) {
|
||||
$response = $this->dispatcher->handleMessage($message);
|
||||
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 (!str_contains($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;
|
||||
}
|
||||
|
||||
fwrite(STDOUT, 'Content-Length: ' . strlen($body) . "\r\n\r\n" . $body);
|
||||
fflush(STDOUT);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user