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
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
- Added `GET /api/diff` endpoint for retrieving structured diffs between two git refs ([#1063](https://github.com/sourcebot-dev/sourcebot/pull/1063))

### Fixed
- Fixed `GET /api/mcp` hanging with zero bytes by returning `405 Method Not Allowed` per the MCP Streamable HTTP spec ([#1064](https://github.com/sourcebot-dev/sourcebot/pull/1064))

## [4.16.3] - 2026-03-27

### Added
Expand Down
43 changes: 9 additions & 34 deletions packages/web/src/app/api/(server)/mcp/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,38 +129,13 @@ export const DELETE = apiHandler(async (request: NextRequest) => {
return result;
});

export const GET = apiHandler(async (request: NextRequest) => {
const result = await sew(() =>
withOptionalAuthV2(async ({ user }) => {
if (env.EXPERIMENT_ASK_GH_ENABLED === 'true' && !user) {
return notAuthenticated();
}
const ownerId = user?.id ?? null;
const sessionId = request.headers.get(MCP_SESSION_ID_HEADER);
if (!sessionId || !sessions.has(sessionId)) {
return {
statusCode: StatusCodes.NOT_FOUND,
errorCode: ErrorCode.NOT_FOUND,
message: 'Session not found.',
} satisfies ServiceError;
}

const session = sessions.get(sessionId)!;
if (session.ownerId !== ownerId) {
return {
statusCode: StatusCodes.FORBIDDEN,
errorCode: ErrorCode.INSUFFICIENT_PERMISSIONS,
message: 'Session does not belong to the authenticated user.',
} satisfies ServiceError;
}

return session.transport.handleRequest(request);
})
);

if (isServiceError(result)) {
return mcpErrorResponse(result);
}

return result;
// Sourcebot does not send server-initiated messages, so the GET SSE stream is not
// supported. Per the MCP Streamable HTTP spec, servers that do not offer a GET SSE
// stream MUST return 405 Method Not Allowed.
// @see: https://modelcontextprotocol.io/specification/2025-03-26/basic/transports#listening-for-messages-from-the-server
export const GET = apiHandler(async (_request: NextRequest) => {
return new Response(null, {
status: StatusCodes.METHOD_NOT_ALLOWED,
headers: { Allow: 'POST, DELETE' },
});
});
Loading