fix(serve): return 405 on GET /mcp instead of 404

MCP Streamable HTTP spec says GET /mcp opens an optional SSE backchannel
for server-initiated messages. gbrain's transport is stateless and
doesn't push server-initiated messages, so per spec we MUST return 405
with Allow: POST, DELETE — not 404. Probing clients (claude.ai, etc.)
distinguish "endpoint exists, no SSE channel" from "endpoint missing"
on this status code; 404 makes them give up.

Cherry-picked from PR #1076.

Co-Authored-By: lukejduncan <lukejduncan@users.noreply.github.com>
This commit is contained in:
Garry Tan
2026-05-18 13:01:51 -07:00
co-authored by lukejduncan
parent 41d9363c72
commit a6534f82e1
+11
View File
@@ -795,6 +795,17 @@ export async function runServeHttp(engine: BrainEngine, options: ServeHttpOption
// ---------------------------------------------------------------------------
const mcpOperations = operations.filter(op => !op.localOnly);
// v0.36.x #1076: MCP Streamable HTTP spec — GET /mcp opens an optional SSE
// backchannel for server-initiated messages. gbrain's transport is stateless
// and doesn't push server-initiated messages, so per spec we MUST return 405
// (not 404) so probing clients (claude.ai, etc.) recognize this as an MCP
// endpoint, not a missing route. Without this, clients display "endpoint not
// found" instead of "endpoint exists but no SSE channel."
app.get('/mcp', (_req: Request, res: Response) => {
res.set('Allow', 'POST, DELETE');
res.status(405).json({ jsonrpc: '2.0', error: { code: -32000, message: 'Method not allowed' }, id: null });
});
app.post('/mcp', requireBearerAuth({ verifier: oauthProvider }), async (req: Request, res: Response) => {
const startTime = Date.now();
const authInfo = (req as any).auth as AuthInfo;