Skip to content

feat(web): add MCP HTTP endpoint with Streamable HTTP transport#976

Merged
brendan-kellam merged 5 commits intomainfrom
brendan/mcp-http-endpoint-SOU-263
Mar 4, 2026
Merged

feat(web): add MCP HTTP endpoint with Streamable HTTP transport#976
brendan-kellam merged 5 commits intomainfrom
brendan/mcp-http-endpoint-SOU-263

Conversation

@brendan-kellam
Copy link
Contributor

@brendan-kellam brendan-kellam commented Mar 4, 2026

Summary

  • Adds /api/mcp route implementing the MCP Streamable HTTP transport with SSE support
  • Adds Bearer token auth to getAuthenticatedUser so MCP clients can authenticate with Sourcebot API keys via Authorization: Bearer <token>
  • Exposes the following MCP tools: ask_codebase, search_code, read_file, list_repos, list_commits, list_tree, list_language_models
  • Extracts server-only chat utilities to utils.server.ts to fix 'use server' module boundary violations
  • Adds withAuthV2 tests for the Bearer token authentication flow

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • HTTP-based MCP session transport and MCP server tools (POST/GET/DELETE).
    • askCodebase: natural-language codebase queries returning portable Markdown answers.
    • New streaming chat flow for richer real-time responses.
  • Improvements

    • Centralized multi-provider language model configuration and safer model metadata.
    • Streamlined chat/message handling, session lifecycle, and error flows.
    • Added Bearer-token auth handling and richer analytics (includes HTTP method).
  • Tests

    • Added tests covering Bearer-token authentication flows.
  • Chores

    • Added dependency: @modelcontextprotocol/sdk.

…263)

- Add /api/mcp route with WebStandardStreamableHTTPServerTransport supporting SSE and JSON responses
- Add Bearer token auth support to getAuthenticatedUser for programmatic MCP clients
- Add session ownership validation to prevent session hijacking (per MCP security spec)
- Extract chat utils to utils.server.ts to fix 'use server' module boundary violations
- Add ask_codebase, search_code, read_file, list_repos, list_commits, list_tree, list_language_models MCP tools
- Add withAuthV2 tests for Bearer token authentication flow

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@github-actions

This comment has been minimized.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 4, 2026

Caution

Review failed

Pull request was closed or merged during review

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Adds Model Context Protocol (MCP) support and a new askCodebase orchestrator, extracts server-side chat utilities and multi-provider LLM initialization, refactors chat streaming to createMessageStream, adds Bearer-token auth handling, and introduces dependency @modelcontextprotocol/sdk.

Changes

Cohort / File(s) Summary
MCP HTTP & Server
packages/web/src/app/api/(server)/mcp/route.ts, packages/web/src/features/mcp/server.ts, packages/web/src/features/mcp/types.ts, packages/web/src/features/mcp/utils.ts
Adds an MCP session-backed HTTP route (POST/GET/DELETE) with in-memory session store, MCP server registration of tools (search_code, list_commits, list_repos, read_file, list_tree, list_language_models, ask_codebase), tree utilities, and ServiceError helpers.
askCodebase Orchestrator
packages/web/src/features/mcp/askCodebase.ts
New askCodebase function: validates configured models, resolves model/provider, creates chat, validates repos, starts a streaming agent, drains stream, generates chat name, persists messages, and returns portable Markdown plus chat metadata.
Chat utils extraction & types
packages/web/src/features/chat/utils.server.ts, packages/web/src/features/chat/actions.ts, packages/web/src/features/chat/types.ts
Moves ownership/sharing checks, message persistence, language-model config loading and AISDK selection into utils.server; actions.ts delegates to these utilities; tightens language model provider enum and model-info schema.
Chat agent / streaming
packages/web/src/features/chat/agent.ts, packages/web/src/app/api/(server)/chat/route.ts, packages/web/src/app/api/(server)/chat/blocking/route.ts
Adds exported createMessageStream with UI stream merge helpers; chat routes simplified to use external streaming utilities; blocking chat route now delegates to askCodebase (previous in-file orchestration removed).
MCP feature wiring & tool usage
packages/web/src/features/mcp/*
Registers MCP tools including ask_codebase which delegates to askCodebase and returns formatted content; integrates model listing and tree/listing tools.
Import path & small-page changes
packages/web/src/app/[domain]/askgh/.../page.tsx, packages/web/src/app/[domain]/browse/layout.tsx, packages/web/src/app/[domain]/chat/[id]/page.tsx, packages/web/src/app/[domain]/chat/page.tsx, packages/web/src/app/[domain]/search/page.tsx, packages/web/src/app/api/(server)/models/route.ts, packages/web/src/features/searchAssist/actions.ts
Repoints getConfiguredLanguageModelsInfo and related helpers from @/features/chat/actions to @/features/chat/utils.server and adjusts immediate service-error handling in a page.
Auth & tests
packages/web/src/withAuthV2.ts, packages/web/src/withAuthV2.test.ts
Adds Bearer token handling in getAuthenticatedUser (API key verification and lastUsedAt update) and expands tests for bearer-token and auth callback flows.
Telemetry
packages/web/src/lib/apiHandler.ts, packages/web/src/lib/posthogEvents.ts
apiHandler now includes HTTP method in captured api_request events; PostHog event schema updated to require method.
Dependency & minor cleanup
packages/web/package.json, packages/web/src/features/chat/utils.ts
Adds dependency @modelcontextprotocol/sdk ^1.27.1; minor import and formatting tweaks in utils.ts.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Client as Web Client
    participant MCP as /api/mcp
    participant Orchestrator as askCodebase
    participant Search as Search Service
    participant LLM as Language Model / Agent
    participant DB as Chat DB

    User->>Client: Submit query (query, repos, model)
    Client->>MCP: POST /api/mcp (ask_codebase)
    MCP->>Orchestrator: askCodebase(params)

    Orchestrator->>Orchestrator: validate configured models
    Orchestrator->>DB: create chat record
    Orchestrator->>Search: resolve repos -> search scopes

    Orchestrator->>LLM: start createMessageStream
    LLM->>Search: execute repo searches
    Search-->>LLM: return results
    LLM-->>Orchestrator: stream message chunks
    Orchestrator->>DB: updateChatMessages(onFinish)
    Orchestrator->>Orchestrator: generate chat name
    Orchestrator->>DB: update chat name & metadata

    Orchestrator-->>MCP: return answer, chatId, chatUrl, modelInfo
    MCP-->>Client: response (markdown answer + link)
    Client-->>User: display answer and session link
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested labels

sourcebot-team

Suggested reviewers

  • msukkari
🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: adding an MCP HTTP endpoint with Streamable HTTP transport. It is specific, concise, and reflects the primary contribution of this PR.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch brendan/mcp-http-endpoint-SOU-263

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/web/src/app/api/(server)/chat/blocking/route.ts (1)

14-29: ⚠️ Potential issue | 🟠 Major

Avoid request-schema drift with MCP package contract.

This route now owns a local blocking schema, but it differs from the MCP-side askCodebaseRequestSchema (packages/mcp/src/schemas.ts), which increases integration drift risk over time. Prefer deriving from a shared schema (or matching it exactly) so /api/chat/blocking and MCP tools stay contract-compatible.

🔧 Minimal alignment patch
-    languageModel: languageModelInfoSchema
-        .optional()
+    languageModel: languageModelInfoSchema
+        .omit({ displayName: true })
+        .optional()
         .describe("The language model to use for the chat. If not provided, the first configured model is used."),
-    visibility: z
-        .nativeEnum(ChatVisibility)
+    visibility: z
+        .enum(['PRIVATE', 'PUBLIC'])
         .optional()
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/web/src/app/api/`(server)/chat/blocking/route.ts around lines 14 -
29, The local blockingChatRequestSchema diverges from the MCP-side
askCodebaseRequestSchema; update this route to reuse the shared contract instead
of duplicating it: import and use askCodebaseRequestSchema (or export a shared
schema from the MCP package) and replace blockingChatRequestSchema with that
imported schema (or derive from it, e.g., z.intersection/extend if you need
small local additions), making sure fields like query, repos, languageModel, and
visibility keep identical types/optional flags and descriptions to avoid schema
drift.
🧹 Nitpick comments (3)
packages/web/src/withAuthV2.ts (1)

119-122: Handle the Authorization scheme case-insensitively for broader client compatibility.

startsWith("Bearer ") is strict; some clients send bearer .... A case-insensitive parse avoids false auth misses.

♻️ Proposed adjustment
-    const authorizationHeader = (await headers()).get("Authorization") ?? undefined;
-    if (authorizationHeader?.startsWith("Bearer ")) {
-        const bearerToken = authorizationHeader.slice(7);
+    const authorizationHeader = (await headers()).get("Authorization") ?? undefined;
+    const bearerMatch = authorizationHeader?.match(/^Bearer\s+(.+)$/i);
+    if (bearerMatch) {
+        const bearerToken = bearerMatch[1].trim();
+        if (!bearerToken) {
+            // continue to other auth methods
+        } else {
         const apiKey = await getVerifiedApiObject(bearerToken);
         if (apiKey) {
             const user = await __unsafePrisma.user.findUnique({
                 where: { id: apiKey.createdById },
                 include: { accounts: true },
             });
             if (user) {
                 await __unsafePrisma.apiKey.update({
                     where: { hash: apiKey.hash },
                     data: { lastUsedAt: new Date() },
                 });
                 return user;
             }
         }
+        }
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/web/src/withAuthV2.ts` around lines 119 - 122, The Authorization
header check using authorizationHeader?.startsWith("Bearer ") is case-sensitive
and can miss valid tokens; update the logic around headers(),
authorizationHeader, bearerToken and getVerifiedApiObject to perform a
case-insensitive scheme check (e.g., normalize authorizationHeader to lowercase
or use a case-insensitive regex) so you detect "Bearer" regardless of case, then
extract the token after the scheme and pass it to getVerifiedApiObject.
packages/web/src/features/chat/actions.ts (1)

513-516: Use the same owner helper in feedback authorization.

Line 515 still uses chat.createdById !== user?.id directly, while other flows now rely on isOwnerOfChat. Reusing the helper here prevents ownership-rule drift.

♻️ Suggested consistency fix
-        const isSharedWithUser = await isChatSharedWithUser({ prisma, chatId, userId: user?.id });
-        if (chat.visibility === ChatVisibility.PRIVATE && chat.createdById !== user?.id && !isSharedWithUser) {
+        const isOwner = await isOwnerOfChat(chat, user);
+        const isSharedWithUser = await isChatSharedWithUser({ prisma, chatId, userId: user?.id });
+        if (chat.visibility === ChatVisibility.PRIVATE && !isOwner && !isSharedWithUser) {
             return notFound();
         }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/web/src/features/chat/actions.ts` around lines 513 - 516, Replace
the direct owner check in the feedback authorization branch with the shared
owner helper: instead of comparing chat.createdById !== user?.id use the
isOwnerOfChat helper (call isOwnerOfChat({ prisma, chatId, userId: user?.id }))
so the condition becomes "chat.visibility === ChatVisibility.PRIVATE &&
!isOwnerOfChat(...) && !isChatSharedWithUser(...)" ensuring ownership logic is
centralized and consistent with other flows.
packages/web/src/features/mcp/server.ts (1)

356-369: Use an index-based queue to avoid shift() costs in BFS.

Line 367-368 repeatedly calls queue.shift(), which reindexes the array each time. For larger trees, this is an avoidable hot-path cost.

⚡ Suggested queue pattern
-            const queue: Array<{ path: string; depth: number }> = [{ path: normalizedPath, depth: 0 }];
+            const queue: Array<{ path: string; depth: number }> = [{ path: normalizedPath, depth: 0 }];
+            let queueHead = 0;

-            while (queue.length > 0 && !truncated) {
-                const currentDepth = queue[0]!.depth;
+            while (queueHead < queue.length && !truncated) {
+                const currentDepth = queue[queueHead]!.depth;
                 const currentLevelPaths: string[] = [];

-                while (queue.length > 0 && queue[0]!.depth === currentDepth) {
-                    currentLevelPaths.push(queue.shift()!.path);
+                while (queueHead < queue.length && queue[queueHead]!.depth === currentDepth) {
+                    currentLevelPaths.push(queue[queueHead]!.path);
+                    queueHead++;
                 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/web/src/features/mcp/server.ts` around lines 356 - 369, The BFS loop
uses array.shift() on the variable queue (declared as const queue: Array<{ path:
string; depth: number }>) which causes O(n) reindexing; replace the shifting
pattern with an index-based queue (e.g., maintain a head numeric pointer) so you
iterate by advancing head and push new items to queue without shifting. Update
the two while loops that rely on queue.shift() (the outer while (queue.length >
0 && !truncated) and the inner grouping loop that builds currentLevelPaths) to
use the head pointer and queue[head] accesses, and ensure queuedPaths,
seenEntries, entries, truncated and treeError logic remains unchanged. Make sure
to stop the loop when head >= queue.length or truncated becomes true and
preserve depth grouping by reading queue[head].depth into currentDepth as done
today.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/web/src/app/`[domain]/askgh/[owner]/[repo]/page.tsx:
- Around line 48-53: The code calls getConfiguredLanguageModelsInfo() before
checking repoInfo for errors, which can do unnecessary work or surface unrelated
failures; reorder and validate repoInfo returned by getRepoInfo() first using
isServiceError(repoInfo) and throw new ServiceErrorException(repoInfo)
immediately if it is an error, only then call getConfiguredLanguageModelsInfo()
and proceed—update the sequence around the repoInfo variable and the
getConfiguredLanguageModelsInfo() invocation so the error guard (isServiceError
and ServiceErrorException) runs before loading language models.

In `@packages/web/src/app/api/`(server)/mcp/route.ts:
- Around line 23-25: The module-level unbounded sessions Map (sessions of type
Map<string, McpSession>) can grow without limit; add a MAX_SESSIONS constant and
bound the store by evicting oldest entries when inserting new sessions (use Map
insertion order to find/delete the first key) and/or add a TTL on McpSession
(e.g., lastAccess timestamp) with a periodic cleanup task; update every place
that calls sessions.set (and any session-creation code) to perform the eviction
check before adding and update lastAccess on reads so stale sessions can be
removed.
- Around line 31-32: Validate the MCP-Session-Id header with a Zod schema before
any lookup/creation: replace direct reads of
request.headers.get(MCP_SESSION_ID_HEADER) with validated value using a
z.string().uuid() (or appropriate pattern) schema and return a
400/Response.badRequest when parsing fails; apply this validation wherever
sessionId is consumed in route.ts (the places that currently assign to sessionId
around the MCP_SESSION_ID_HEADER reads and any subsequent session
lookup/creation logic) so downstream code never operates on a malformed
identifier.

In `@packages/web/src/features/chat/agent.ts`:
- Around line 69-87: The current messages.map callback in the module uses
conditional returns and sometimes falls through without returning, triggering
the useIterableCallbackReturn lint error; replace the map+filter with
messages.flatMap(...) so the callback always returns an array: return [] when no
value should be emitted, or return [{ role: 'user', content: ... }] or [{ role:
'assistant', content: [answerPart] }] when producing an item, using the same
logic around message.role and getAnswerPartFromAssistantMessage to locate the
correct branch and ensure no undefined values remain.

In `@packages/web/src/features/mcp/askCodebase.ts`:
- Around line 78-85: The chat record is created with prisma.chat.create before
repo validation runs and ServiceErrorException can be thrown, causing orphan
chats on invalid input; move the repo validation logic (the code that can throw
ServiceErrorException for invalid repos) to run before calling
prisma.chat.create so you only persist the chat after repos are confirmed valid,
and if validation fails do not call prisma.chat.create (ensure the same data
fields like orgId, createdById, visibility, messages remain used when creating
the chat after validation).
- Around line 105-107: The repo lookup using prisma.repo.findFirst (in
askCodebase.ts) filters only by name, allowing cross-org access; update the
where clause passed to prisma.repo.findFirst (the repoDB lookup) to include
orgId: org.id (using the org from withOptionalAuthV2) so the query becomes
scoped to the current organization.

---

Outside diff comments:
In `@packages/web/src/app/api/`(server)/chat/blocking/route.ts:
- Around line 14-29: The local blockingChatRequestSchema diverges from the
MCP-side askCodebaseRequestSchema; update this route to reuse the shared
contract instead of duplicating it: import and use askCodebaseRequestSchema (or
export a shared schema from the MCP package) and replace
blockingChatRequestSchema with that imported schema (or derive from it, e.g.,
z.intersection/extend if you need small local additions), making sure fields
like query, repos, languageModel, and visibility keep identical types/optional
flags and descriptions to avoid schema drift.

---

Nitpick comments:
In `@packages/web/src/features/chat/actions.ts`:
- Around line 513-516: Replace the direct owner check in the feedback
authorization branch with the shared owner helper: instead of comparing
chat.createdById !== user?.id use the isOwnerOfChat helper (call isOwnerOfChat({
prisma, chatId, userId: user?.id })) so the condition becomes "chat.visibility
=== ChatVisibility.PRIVATE && !isOwnerOfChat(...) && !isChatSharedWithUser(...)"
ensuring ownership logic is centralized and consistent with other flows.

In `@packages/web/src/features/mcp/server.ts`:
- Around line 356-369: The BFS loop uses array.shift() on the variable queue
(declared as const queue: Array<{ path: string; depth: number }>) which causes
O(n) reindexing; replace the shifting pattern with an index-based queue (e.g.,
maintain a head numeric pointer) so you iterate by advancing head and push new
items to queue without shifting. Update the two while loops that rely on
queue.shift() (the outer while (queue.length > 0 && !truncated) and the inner
grouping loop that builds currentLevelPaths) to use the head pointer and
queue[head] accesses, and ensure queuedPaths, seenEntries, entries, truncated
and treeError logic remains unchanged. Make sure to stop the loop when head >=
queue.length or truncated becomes true and preserve depth grouping by reading
queue[head].depth into currentDepth as done today.

In `@packages/web/src/withAuthV2.ts`:
- Around line 119-122: The Authorization header check using
authorizationHeader?.startsWith("Bearer ") is case-sensitive and can miss valid
tokens; update the logic around headers(), authorizationHeader, bearerToken and
getVerifiedApiObject to perform a case-insensitive scheme check (e.g., normalize
authorizationHeader to lowercase or use a case-insensitive regex) so you detect
"Bearer" regardless of case, then extract the token after the scheme and pass it
to getVerifiedApiObject.

ℹ️ Review info
Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: d59faa7e-6e2a-4a29-8e24-530d66940848

📥 Commits

Reviewing files that changed from the base of the PR and between d9d5b2a and 49fc1b5.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (22)
  • packages/web/package.json
  • packages/web/src/app/[domain]/askgh/[owner]/[repo]/page.tsx
  • packages/web/src/app/[domain]/browse/layout.tsx
  • packages/web/src/app/[domain]/chat/[id]/page.tsx
  • packages/web/src/app/[domain]/chat/page.tsx
  • packages/web/src/app/[domain]/search/page.tsx
  • packages/web/src/app/api/(server)/chat/blocking/route.ts
  • packages/web/src/app/api/(server)/chat/route.ts
  • packages/web/src/app/api/(server)/mcp/route.ts
  • packages/web/src/app/api/(server)/models/route.ts
  • packages/web/src/features/chat/actions.ts
  • packages/web/src/features/chat/agent.ts
  • packages/web/src/features/chat/types.ts
  • packages/web/src/features/chat/utils.server.ts
  • packages/web/src/features/chat/utils.ts
  • packages/web/src/features/mcp/askCodebase.ts
  • packages/web/src/features/mcp/server.ts
  • packages/web/src/features/mcp/types.ts
  • packages/web/src/features/mcp/utils.ts
  • packages/web/src/features/searchAssist/actions.ts
  • packages/web/src/withAuthV2.test.ts
  • packages/web/src/withAuthV2.ts

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/web/src/app/api/`(server)/mcp/route.ts:
- Around line 29-42: The current ownership check lets all unauthenticated
requests share ownerId = null and access sessions; change the logic in the route
that uses ownerId, sessions, MCP_SESSION_ID_HEADER, session.ownerId and
transport.handleRequest so unauthenticated requests cannot match null ownerId.
Two concise fixes: (A) Preferred: reject requests with no authenticated user (if
auth is required) by returning the ServiceError when user is falsy before any
session lookup; or (B) If anonymous sessions are allowed, require a
client-provided anonymous identifier (e.g., read X-Anonymous-Id header), store
it as session.anonymousId when creating sessions, and on access require
session.anonymousId === requestAnonymousId instead of comparing null ownerId.
Implement one of these in the code paths that currently compare session.ownerId
!== ownerId and in the session-creation/lookup branches referenced in this file.
- Around line 46-57: The onsessionclosed handler currently only removes the
session from the sessions map, leaking resources; update the onsessionclosed
implementation to retrieve the stored session entry (from sessions by
closedSessionId) and asynchronously call the session's server.close() and
transport.close() (handle/await errors), then delete the session; ensure the
session was created with createMcpServer() and
WebStandardStreamableHTTPServerTransport so you reference the same mcpServer and
transport instances stored in sessions when performing the cleanup.

ℹ️ Review info
Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: ea6f37c3-c91d-405c-9e07-18508e38804d

📥 Commits

Reviewing files that changed from the base of the PR and between 49fc1b5 and f2b5d1c.

📒 Files selected for processing (3)
  • packages/web/src/app/api/(server)/mcp/route.ts
  • packages/web/src/lib/apiHandler.ts
  • packages/web/src/lib/posthogEvents.ts

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
CHANGELOG.md (1)

11-11: Fix wording/formatting in changelog entry text.

Line 11 has a small grammar/style issue (support a and lowercase http), which makes the entry read awkwardly.

✏️ Suggested edit
-- Added support a MCP streamable http transport hosted at `/api/mcp`. [`#976`](https://github.com/sourcebot-dev/sourcebot/pull/976)
+- Added support for an MCP streamable HTTP transport hosted at `/api/mcp`. [`#976`](https://github.com/sourcebot-dev/sourcebot/pull/976)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@CHANGELOG.md` at line 11, Update the changelog entry sentence "Added support
a MCP streamable http transport hosted at `/api/mcp`." to correct grammar and
capitalization: change "support a" to "support for an" and "http" to "HTTP" so
it reads e.g. "Added support for an MCP streamable HTTP transport hosted at
`/api/mcp`."; modify the exact line containing that sentence in CHANGELOG.md.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@CHANGELOG.md`:
- Line 11: Update the changelog entry sentence "Added support a MCP streamable
http transport hosted at `/api/mcp`." to correct grammar and capitalization:
change "support a" to "support for an" and "http" to "HTTP" so it reads e.g.
"Added support for an MCP streamable HTTP transport hosted at `/api/mcp`.";
modify the exact line containing that sentence in CHANGELOG.md.

ℹ️ Review info
Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: e6b0d3a5-5cd5-43ad-8d53-0eac4ca008c8

📥 Commits

Reviewing files that changed from the base of the PR and between f2b5d1c and 316b685.

📒 Files selected for processing (1)
  • CHANGELOG.md

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (1)
packages/web/src/features/mcp/askCodebase.ts (1)

78-124: ⚠️ Potential issue | 🟠 Major

Validate repos before creating the chat record to avoid orphan chats.

The chat record is created at lines 78-85, but repo validation at lines 104-124 can throw ServiceErrorException for invalid repos. This leaves orphan chat records in the database when validation fails.

Move the repo validation block before the chat creation:

🐛 Suggested reorder
+            const selectedRepos = (await Promise.all(repos.map(async (repo) => {
+                const repoDB = await prisma.repo.findFirst({
+                    where: {
+                        name: repo,
+                        orgId: org.id,
+                    },
+                });
+                if (!repoDB) {
+                    throw new ServiceErrorException({
+                        statusCode: StatusCodes.BAD_REQUEST,
+                        errorCode: ErrorCode.INVALID_REQUEST_BODY,
+                        message: `Repository '${repo}' not found.`,
+                    });
+                }
+                return {
+                    type: 'repo',
+                    value: repoDB.name,
+                    name: repoDB.displayName ?? repoDB.name.split('/').pop() ?? repoDB.name,
+                    codeHostType: repoDB.external_codeHostType,
+                } satisfies SearchScope;
+            })));
+
             const chat = await prisma.chat.create({
                 data: {
                     orgId: org.id,
                     createdById: user?.id,
                     visibility: chatVisibility,
                     messages: [] as unknown as Prisma.InputJsonValue,
                 },
             });

             await captureEvent('wa_chat_thread_created', {
                 chatId: chat.id,
                 isAnonymous: !user,
             });
-
-            // ... (logging and userMessage creation remain here) ...
-
-            const selectedRepos = (await Promise.all(repos.map(async (repo) => {
-                const repoDB = await prisma.repo.findFirst({
-                    where: {
-                        name: repo,
-                        orgId: org.id,
-                    },
-                });
-                if (!repoDB) {
-                    throw new ServiceErrorException({
-                        statusCode: StatusCodes.BAD_REQUEST,
-                        errorCode: ErrorCode.INVALID_REQUEST_BODY,
-                        message: `Repository '${repo}' not found.`,
-                    });
-                }
-                return {
-                    type: 'repo',
-                    value: repoDB.name,
-                    name: repoDB.displayName ?? repoDB.name.split('/').pop() ?? repoDB.name,
-                    codeHostType: repoDB.external_codeHostType,
-                } satisfies SearchScope;
-            })));
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/web/src/features/mcp/askCodebase.ts` around lines 78 - 124, The chat
record is created with prisma.chat.create before validating repos, so invalid
repo names can throw ServiceErrorException (from the repos map block) and leave
orphan chats; move the repo validation/lookup (the Promise.all over repos that
queries prisma.repo.findFirst and throws ServiceErrorException) to run before
calling prisma.chat.create, then use the validated selectedRepos when
creating/continuing the chat and only call
captureEvent('wa_chat_thread_created', ...) and start the agent after validation
succeeds.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/web/src/features/mcp/askCodebase.ts`:
- Around line 163-169: The Promise.all combining blockStreamUntilFinish(stream)
and generateChatNameFromMessage(...) can reject if generateChatNameFromMessage
throws, preventing updateChatMessages from running; modify the logic so you
await blockStreamUntilFinish(stream) independently and call
generateChatNameFromMessage in a try/catch (or use .catch) to fall back to a
default name on error, then call updateChatMessages with the streamed messages
and the determined name; specifically change the call that uses Promise.all to
first await blockStreamUntilFinish(stream) and then safely obtain name via
generateChatNameFromMessage with error handling, referencing the functions
blockStreamUntilFinish, generateChatNameFromMessage, and updateChatMessages.
- Around line 147-149: The onFinish callback passed to createMessageStream is
async but not awaited, causing a race where blockStreamUntilFinish can finish
before finalMessages is populated; fix this by making onFinish return or resolve
a Promise that you await after blockStreamUntilFinish (or by awaiting the
createMessageStream/reader completion that includes onFinish). Concretely: in
the stream setup around createMessageStream and blockStreamUntilFinish, change
the onFinish handler (currently setting finalMessages) to resolve an externally
created Promise (e.g., finishPromise) and then await that finishPromise after
blockStreamUntilFinish completes so finalMessages is guaranteed populated before
you use/save it; reference createMessageStream, onFinish,
blockStreamUntilFinish, and finalMessages to locate the changes.

---

Duplicate comments:
In `@packages/web/src/features/mcp/askCodebase.ts`:
- Around line 78-124: The chat record is created with prisma.chat.create before
validating repos, so invalid repo names can throw ServiceErrorException (from
the repos map block) and leave orphan chats; move the repo validation/lookup
(the Promise.all over repos that queries prisma.repo.findFirst and throws
ServiceErrorException) to run before calling prisma.chat.create, then use the
validated selectedRepos when creating/continuing the chat and only call
captureEvent('wa_chat_thread_created', ...) and start the agent after validation
succeeds.

ℹ️ Review info
Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 0dafb4b6-f261-4a59-b143-4d49afa158d8

📥 Commits

Reviewing files that changed from the base of the PR and between 316b685 and 425b7bf.

📒 Files selected for processing (1)
  • packages/web/src/features/mcp/askCodebase.ts

@brendan-kellam brendan-kellam merged commit f7ba084 into main Mar 4, 2026
9 of 10 checks passed
@brendan-kellam brendan-kellam deleted the brendan/mcp-http-endpoint-SOU-263 branch March 4, 2026 04:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant