Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions modules/mcp/controllers/ServerController.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,25 @@ public function indexAction()
return;
}

// A non-POST (a browser GET, a link click) isn't a JSON-RPC call. Per the Streamable HTTP transport,
// GET is for an SSE stream we don't offer in v1 → 405, but with a human-readable "what is this + how
// to use it" body so hitting /mcp in a browser explains itself instead of a cryptic parse error.
if (strtoupper((string) $this->getRequest()->getMethod()) !== 'POST') {
$resp->setHttpResponseCode(405);
$resp->setHeader('Allow', 'POST', true);
$this->_emit([
'name' => 'Tiger',
'version' => Tiger_Version::VERSION,
'protocolVersion' => Tiger_Mcp::PROTOCOL_VERSION,
'transport' => 'streamable-http',
'message' => 'This is Tiger\'s MCP endpoint. POST a JSON-RPC 2.0 request (Content-Type: '
. 'application/json) — e.g. {"jsonrpc":"2.0","id":1,"method":"initialize"}. '
. 'Interactive GET/SSE is not supported in v1; connect an MCP client, or test '
. 'with the MCP Inspector (npx @modelcontextprotocol/inspector) or curl.',
]);
return;
}

$msg = json_decode($this->_rawBody(), true);
if (!is_array($msg)) {
$resp->setHttpResponseCode(400);
Expand Down
11 changes: 11 additions & 0 deletions tests/Integration/Mcp/McpControllerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,17 @@ public function the_endpoint_is_404_when_disabled(): void
$this->assertSame(404, $code, 'off by default');
}

#[Test]
public function a_browser_get_returns_a_helpful_405_not_a_parse_error(): void
{
$this->enableMcp();
$res = $this->dispatchAction(FakeMcpController::class, 'index', [], 'GET');
$this->assertSame(405, $res->getHttpResponseCode(), 'GET is not a JSON-RPC call');
$out = json_decode($this->echoed, true);
$this->assertSame('Tiger', $out['name']);
$this->assertStringContainsString('POST a JSON-RPC', $out['message']);
}

#[Test]
public function initialize_returns_serverinfo_when_enabled(): void
{
Expand Down
Loading