Expand redMCP safe issue operations and HTTP handling
This commit is contained in:
@@ -23,8 +23,8 @@ final class McpHttpHandler
|
||||
$this->sendJson(404, ['error' => 'not found']);
|
||||
return;
|
||||
}
|
||||
if (($_SERVER['REQUEST_METHOD'] ?? '') !== 'POST') {
|
||||
$this->sendJson(405, ['error' => 'method not allowed']);
|
||||
if (!$this->originAllowed()) {
|
||||
$this->sendJson(403, ['error' => 'origin not allowed']);
|
||||
return;
|
||||
}
|
||||
if (!$this->authorized()) {
|
||||
@@ -32,6 +32,16 @@ final class McpHttpHandler
|
||||
$this->sendJson(401, ['error' => 'unauthorized']);
|
||||
return;
|
||||
}
|
||||
if (($_SERVER['REQUEST_METHOD'] ?? '') === 'GET') {
|
||||
header('Allow: POST');
|
||||
$this->sendJson(405, ['error' => 'method not allowed']);
|
||||
return;
|
||||
}
|
||||
if (($_SERVER['REQUEST_METHOD'] ?? '') !== 'POST') {
|
||||
header('Allow: POST');
|
||||
$this->sendJson(405, ['error' => 'method not allowed']);
|
||||
return;
|
||||
}
|
||||
|
||||
$raw = file_get_contents('php://input');
|
||||
$decoded = json_decode(is_string($raw) ? $raw : '', true);
|
||||
@@ -56,6 +66,10 @@ final class McpHttpHandler
|
||||
http_response_code(202);
|
||||
return;
|
||||
}
|
||||
if ($this->acceptsEventStream()) {
|
||||
$this->sendEventStream($responses);
|
||||
return;
|
||||
}
|
||||
$this->sendJson(200, $responses);
|
||||
return;
|
||||
}
|
||||
@@ -66,6 +80,10 @@ final class McpHttpHandler
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->acceptsEventStream()) {
|
||||
$this->sendEventStream([$response]);
|
||||
return;
|
||||
}
|
||||
$this->sendJson(200, $response);
|
||||
}
|
||||
|
||||
@@ -79,6 +97,43 @@ final class McpHttpHandler
|
||||
return hash_equals($this->token, substr($header, 7));
|
||||
}
|
||||
|
||||
private function originAllowed(): bool
|
||||
{
|
||||
$origin = $_SERVER['HTTP_ORIGIN'] ?? '';
|
||||
if (!is_string($origin) || trim($origin) === '') {
|
||||
return true;
|
||||
}
|
||||
|
||||
$origin = trim($origin);
|
||||
foreach ($this->allowedOrigins() as $allowedOrigin) {
|
||||
if (hash_equals($allowedOrigin, $origin)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
$host = parse_url($origin, PHP_URL_HOST);
|
||||
return is_string($host) && in_array(strtolower($host), ['localhost', '127.0.0.1', '::1'], true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int,string>
|
||||
*/
|
||||
private function allowedOrigins(): array
|
||||
{
|
||||
$raw = getenv('MCP_ALLOWED_ORIGINS') ?: '';
|
||||
if (!is_string($raw) || trim($raw) === '') {
|
||||
return [];
|
||||
}
|
||||
|
||||
return array_values(array_filter(array_map('trim', explode(',', $raw))));
|
||||
}
|
||||
|
||||
private function acceptsEventStream(): bool
|
||||
{
|
||||
$accept = $_SERVER['HTTP_ACCEPT'] ?? '';
|
||||
return is_string($accept) && stripos($accept, 'text/event-stream') !== false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string,mixed>
|
||||
*/
|
||||
@@ -100,6 +155,27 @@ final class McpHttpHandler
|
||||
echo json_encode($payload, JSON_UNESCAPED_SLASHES);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int,array<string,mixed>> $messages
|
||||
*/
|
||||
private function sendEventStream(array $messages): void
|
||||
{
|
||||
http_response_code(200);
|
||||
header('Content-Type: text/event-stream');
|
||||
header('Cache-Control: no-cache');
|
||||
header('X-Accel-Buffering: no');
|
||||
|
||||
foreach ($messages as $message) {
|
||||
$encoded = json_encode($message, JSON_UNESCAPED_SLASHES);
|
||||
if ($encoded === false) {
|
||||
continue;
|
||||
}
|
||||
echo "event: message\n";
|
||||
echo 'data: ' . $encoded . "\n\n";
|
||||
flush();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string,mixed>
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user