Skip to content

Chat sessions#1628

Merged
chelojimenez merged 9 commits intomainfrom
pii-scrubbing
Mar 17, 2026
Merged

Chat sessions#1628
chelojimenez merged 9 commits intomainfrom
pii-scrubbing

Conversation

@chelojimenez
Copy link
Copy Markdown
Contributor

@chelojimenez chelojimenez commented Mar 17, 2026

Note

Medium Risk
Introduces new persistence/network calls and changes hosted auth/session token handling (including sandbox/shared flows), which could affect chat streaming completion and Convex ingestion reliability.

Overview
Chat requests now carry a chatSessionId (and hosted requests may include sandboxToken), enabling session-based persistence and sandbox/shared flows across client and server.

On the server, both web/chat-v2 and mcp/chat-v2 persist completed conversations to Convex via a new persistChatSessionToConvex helper (replacing saveThreadToConvex), including BYOK streamText completions via onFinish; MCPJam streaming skips per-step ingestion and instead persists once at the end of the agent loop. Client shared-chat hooks are updated to query/mutate chatSessions:* APIs, treat sandbox sessions like shared sessions for guest behavior and widget capture, and add retry handling when snapshot creation returns null or reports a missing session.

Written by Cursor Bugbot for commit 1718f18. This will update automatically on new commits. Configure here.

@dosubot dosubot Bot added the size:L This PR changes 100-499 lines, ignoring generated files. label Mar 17, 2026
@railway-app
Copy link
Copy Markdown

railway-app Bot commented Mar 17, 2026

🚅 Environment inspector-pr-1628 in triumphant-alignment has no services deployed.

1 service not affected by this PR
  • mcp-inspector

@chelojimenez
Copy link
Copy Markdown
Contributor Author

chelojimenez commented Mar 17, 2026

Snyk checks have passed. No issues have been found so far.

Status Scan Engine Critical High Medium Low Total (0)
Open Source Security 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

@dosubot dosubot Bot added the enhancement New feature or request label Mar 17, 2026
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 17, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: feaee76b-e293-4b55-bf41-70b368a8e006

📥 Commits

Reviewing files that changed from the base of the PR and between 34132e8 and 567a423.

📒 Files selected for processing (19)
  • mcpjam-inspector/client/src/components/evals/run-detail-view.tsx
  • mcpjam-inspector/client/src/hooks/__tests__/use-chat-session.hosted.test.tsx
  • mcpjam-inspector/client/src/hooks/__tests__/useSharedChatWidgetCapture.test.tsx
  • mcpjam-inspector/client/src/hooks/use-chat-session.ts
  • mcpjam-inspector/client/src/hooks/useSharedChatWidgetCapture.ts
  • mcpjam-inspector/server/routes/mcp/__tests__/chat-v2.test.ts
  • mcpjam-inspector/server/routes/mcp/chat-v2.ts
  • mcpjam-inspector/server/routes/web/__tests__/guest-jwks.test.ts
  • mcpjam-inspector/server/routes/web/__tests__/guest-session.test.ts
  • mcpjam-inspector/server/routes/web/chat-v2.ts
  • mcpjam-inspector/server/services/guest-token-verifier.ts
  • mcpjam-inspector/server/utils/__tests__/chat-ingestion.test.ts
  • mcpjam-inspector/server/utils/__tests__/guest-auth.test.ts
  • mcpjam-inspector/server/utils/__tests__/guest-session-source.test.ts
  • mcpjam-inspector/server/utils/chat-ingestion.ts
  • mcpjam-inspector/server/utils/convex-guest-auth-sync.ts
  • mcpjam-inspector/server/utils/guest-session-secret.ts
  • mcpjam-inspector/server/utils/guest-session-source.ts
  • mcpjam-inspector/server/utils/mcpjam-stream-handler.ts

Walkthrough

This PR renames client endpoints/payloads from sharedChatThreads to chatSessions and updates related interfaces (e.g., threadIdsessionId, several fields made optional). It adds hosted sandbox token support to session handling and widget capture, implements retry logic for pending snapshots, removes the old shared-chat persistence module and introduces persistChatSessionToConvex (with timeout, sanitization, and richer payloads), adds ingestion and retry tests, and sets skipChatIngestion on streaming requests; plus assorted test and formatting tweaks.

📝 Coding Plan
  • Generate coding plan for human review comments

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
Copy Markdown
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: 4

Caution

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

⚠️ Outside diff range comments (3)
mcpjam-inspector/server/routes/web/chat-v2.ts (1)

226-235: ⚠️ Potential issue | 🟠 Major

Reject requests that include both shareToken and sandboxToken.

Current precedence silently chooses "serverShare" when both tokens are present, which can misclassify session origin and persistence metadata.

Proposed fix
     const {
       messages,
       model,
       systemPrompt,
       temperature,
       requireToolApproval,
       selectedServerIds,
       shareToken,
       sandboxToken,
     } = body;
+
+    if (shareToken && sandboxToken) {
+      throw new WebRouteError(
+        400,
+        ErrorCode.VALIDATION_ERROR,
+        "shareToken and sandboxToken are mutually exclusive",
+      );
+    }

Also applies to: 324-330

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@mcpjam-inspector/server/routes/web/chat-v2.ts` around lines 226 - 235, When
parsing the request (where body is destructured into messages, model,
systemPrompt, temperature, requireToolApproval, selectedServerIds, shareToken,
sandboxToken) add an early validation that rejects the request with a 400 Bad
Request if both shareToken and sandboxToken are present; do the same in the
later decision branch that currently prefers "serverShare" when both tokens
exist (the block around the logic that classifies session origin/persistence).
Return a clear error message indicating mutually exclusive tokens and ensure
both places short-circuit before any default precedence logic runs.
mcpjam-inspector/client/src/hooks/__tests__/useSharedChatWidgetCapture.test.tsx (1)

30-53: 🛠️ Refactor suggestion | 🟠 Major

Align this test with repository mock presets.

This setup uses bespoke inline mocks instead of the shared test presets expected for client tests.

As per coding guidelines: mcpjam-inspector/client/**/__tests__/*.test.{ts,tsx}: "Use mock presets from client/src/test/mocks/ including mcpApiPresets and storePresets in client tests".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@mcpjam-inspector/client/src/hooks/__tests__/useSharedChatWidgetCapture.test.tsx`
around lines 30 - 53, The test is using bespoke inline mocks (vi.useFakeTimers
setup, manual mockGenerateSnapshotUploadUrl/mockCreateWidgetSnapshot
implementations, and overriding global.fetch, plus direct
useWidgetDebugStore.setState) instead of the repository's shared test presets;
replace the inline mock setup by importing and applying the client test presets
(mcpApiPresets and storePresets from the client test mocks) at the start of the
beforeEach for the useSharedChatWidgetCapture suite, remove the custom
mockGenerateSnapshotUploadUrl, mockCreateWidgetSnapshot and global.fetch
overrides, and initialize widget store state via the storePresets API so the
preset implementations provide consistent mocked behavior for
generateSnapshotUploadUrl, createWidgetSnapshot and network fetches used by the
hook.
mcpjam-inspector/client/src/hooks/use-chat-session.ts (1)

363-378: ⚠️ Potential issue | 🟠 Major

Add sandboxToken to the buildHostedBody function.

The hosted path in /api/web/chat-v2 destructures sandboxToken from the request body and uses it to determine the session's sourceType during persistence. However, buildHostedBody() spreads shareToken but omits hostedSandboxToken, preventing sandbox session tracking from working correctly.

Fix
return {
  workspaceId: hostedWorkspaceId,
  chatSessionId,
  selectedServerIds: hostedSelectedServerIds,
  accessScope: "chat_v2" as const,
  ...(hostedShareToken ? { shareToken: hostedShareToken } : {}),
+ ...(hostedSandboxToken ? { sandboxToken: hostedSandboxToken } : {}),
  ...(hostedOAuthTokens && Object.keys(hostedOAuthTokens).length > 0
    ? { oauthTokens: hostedOAuthTokens }
    : {}),
};
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@mcpjam-inspector/client/src/hooks/use-chat-session.ts` around lines 363 -
378, The hosted request body built by buildHostedBody is missing sandboxToken
(hostedSandboxToken), so add sandboxToken: hostedSandboxToken to the returned
object for the hosted branch; include it similarly to shareToken (conditionally
spread only when present) and keep the existing selectedServerIds, accessScope,
and oauthTokens logic so the server can detect sandbox sessions when persisting
sourceType.
🧹 Nitpick comments (1)
mcpjam-inspector/client/src/hooks/useSharedChatWidgetCapture.ts (1)

329-331: Make retry gating less brittle than raw message matching.

message.includes("Session not found") is fragile; prefer a stable error code/classification from the mutation response when available.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@mcpjam-inspector/client/src/hooks/useSharedChatWidgetCapture.ts` around lines
329 - 331, The retry gating currently checks a fragile substring on the error
text in useSharedChatWidgetCapture (message.includes("Session not found"));
instead, read a stable error identifier from the mutation/response object (e.g.,
error.code, error.name, or response.error.code) and use that to decide retries
for the given toolCallId and retryCountRef; update the conditional that
references message.includes("Session not found") to check the canonical error
field (with a safe fallback to the message only if the canonical field is
missing) so the retry logic uses a durable error code/classification rather than
raw text.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@mcpjam-inspector/client/src/hooks/useSharedChatThreads.ts`:
- Around line 12-17: The contract now makes userId, visitorDisplayName, modelId
and firstMessagePreview optional, so update useSharedChatThreads (the hook that
maps/returns thread objects) to supply defensive defaults for any missing values
before UI consumption: e.g., normalize undefined visitorDisplayName to
"Visitor", modelId to "default-model" or a human-readable model name,
firstMessagePreview to an empty string or "(no preview)", and userId to a stable
placeholder like "unknown" or null-safe handling; ensure the returned thread
shape from useSharedChatThreads always contains these fallback values so
downstream render paths never receive undefined and can render predictably.

In `@mcpjam-inspector/server/utils/chat-ingestion.ts`:
- Around line 81-83: The code currently captures and logs the full ingestion
response body via responseText (set from await response.text()) along with
response.status; change this to avoid storing/logging full response bodies by
replacing responseText with a bounded and sanitized diagnostic: read the
response text but truncate it to a small length (e.g., first 200 chars),
normalize whitespace/newlines, and mask obvious secrets (e.g., API keys, tokens,
emails) before logging, or alternatively log only response.status plus
response.headers or a short status message; update the assignment that sets
responseText and any log callers to use this truncated/sanitized value instead
of the full body.
- Around line 36-74: The POST to `${convexUrl}/ingest-chat` uses a plain fetch
(const response = await fetch(...)) with no timeout; wrap the fetch in an
AbortController: create controller = new AbortController(), pass signal:
controller.signal into the fetch options, start a timer (e.g. setTimeout) that
calls controller.abort() after a configurable timeout (choose a sensible default
or read from options), and clearTimeout(controller) after fetch completes; also
ensure you handle the abort case (catch AbortError) so ingestion failures due to
timeout are surfaced/handled consistently.

In `@mcpjam-inspector/server/utils/mcpjam-stream-handler.ts`:
- Around line 689-691: The MCP route call to handleMCPJamFreeChatModel is
currently passing skipChatIngestion: true and no onConversationComplete handler,
so MCP conversations are never persisted; either add the same conditional
persistence callback used in web routes or document the intentional exclusion.
Specifically, update the handleMCPJamFreeChatModel invocation to accept an
onConversationComplete function (matching the signature used in
web/routes/web/chat-v2.ts) and call your backend persistence when chatSessionId
is present, or add a clear comment near the call site (and keep
skipChatIngestion semantics) explaining why MCP conversations must not be
persisted; refer to the handleMCPJamFreeChatModel call, the skipChatIngestion
flag, and onConversationComplete callback to implement this change.

---

Outside diff comments:
In
`@mcpjam-inspector/client/src/hooks/__tests__/useSharedChatWidgetCapture.test.tsx`:
- Around line 30-53: The test is using bespoke inline mocks (vi.useFakeTimers
setup, manual mockGenerateSnapshotUploadUrl/mockCreateWidgetSnapshot
implementations, and overriding global.fetch, plus direct
useWidgetDebugStore.setState) instead of the repository's shared test presets;
replace the inline mock setup by importing and applying the client test presets
(mcpApiPresets and storePresets from the client test mocks) at the start of the
beforeEach for the useSharedChatWidgetCapture suite, remove the custom
mockGenerateSnapshotUploadUrl, mockCreateWidgetSnapshot and global.fetch
overrides, and initialize widget store state via the storePresets API so the
preset implementations provide consistent mocked behavior for
generateSnapshotUploadUrl, createWidgetSnapshot and network fetches used by the
hook.

In `@mcpjam-inspector/client/src/hooks/use-chat-session.ts`:
- Around line 363-378: The hosted request body built by buildHostedBody is
missing sandboxToken (hostedSandboxToken), so add sandboxToken:
hostedSandboxToken to the returned object for the hosted branch; include it
similarly to shareToken (conditionally spread only when present) and keep the
existing selectedServerIds, accessScope, and oauthTokens logic so the server can
detect sandbox sessions when persisting sourceType.

In `@mcpjam-inspector/server/routes/web/chat-v2.ts`:
- Around line 226-235: When parsing the request (where body is destructured into
messages, model, systemPrompt, temperature, requireToolApproval,
selectedServerIds, shareToken, sandboxToken) add an early validation that
rejects the request with a 400 Bad Request if both shareToken and sandboxToken
are present; do the same in the later decision branch that currently prefers
"serverShare" when both tokens exist (the block around the logic that classifies
session origin/persistence). Return a clear error message indicating mutually
exclusive tokens and ensure both places short-circuit before any default
precedence logic runs.

---

Nitpick comments:
In `@mcpjam-inspector/client/src/hooks/useSharedChatWidgetCapture.ts`:
- Around line 329-331: The retry gating currently checks a fragile substring on
the error text in useSharedChatWidgetCapture (message.includes("Session not
found")); instead, read a stable error identifier from the mutation/response
object (e.g., error.code, error.name, or response.error.code) and use that to
decide retries for the given toolCallId and retryCountRef; update the
conditional that references message.includes("Session not found") to check the
canonical error field (with a safe fallback to the message only if the canonical
field is missing) so the retry logic uses a durable error code/classification
rather than raw text.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 1c46c95a-4bf5-4438-824c-e72c7d0c2da9

📥 Commits

Reviewing files that changed from the base of the PR and between 6593409 and 34132e8.

📒 Files selected for processing (10)
  • mcpjam-inspector/client/src/hooks/__tests__/useSharedChatWidgetCapture.test.tsx
  • mcpjam-inspector/client/src/hooks/use-chat-session.ts
  • mcpjam-inspector/client/src/hooks/useSharedChatThreads.ts
  • mcpjam-inspector/client/src/hooks/useSharedChatWidgetCapture.ts
  • mcpjam-inspector/server/routes/mcp/chat-v2.ts
  • mcpjam-inspector/server/routes/web/chat-v2.ts
  • mcpjam-inspector/server/utils/__tests__/chat-ingestion.test.ts
  • mcpjam-inspector/server/utils/chat-ingestion.ts
  • mcpjam-inspector/server/utils/mcpjam-stream-handler.ts
  • mcpjam-inspector/server/utils/shared-chat-persistence.ts
💤 Files with no reviewable changes (1)
  • mcpjam-inspector/server/utils/shared-chat-persistence.ts

Comment on lines +12 to 17
userId?: string;
visitorDisplayName?: string;
modelId?: string;
messageCount: number;
firstMessagePreview: string;
firstMessagePreview?: string;
startedAt: number;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Optionalized thread fields need defensive UI fallbacks downstream.

Now that these fields are nullable in the contract, any direct render paths should default missing values (e.g., model/display name) to avoid blank/undefined UI.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@mcpjam-inspector/client/src/hooks/useSharedChatThreads.ts` around lines 12 -
17, The contract now makes userId, visitorDisplayName, modelId and
firstMessagePreview optional, so update useSharedChatThreads (the hook that
maps/returns thread objects) to supply defensive defaults for any missing values
before UI consumption: e.g., normalize undefined visitorDisplayName to
"Visitor", modelId to "default-model" or a human-readable model name,
firstMessagePreview to an empty string or "(no preview)", and userId to a stable
placeholder like "unknown" or null-safe handling; ensure the returned thread
shape from useSharedChatThreads always contains these fallback values so
downstream render paths never receive undefined and can render predictably.

Comment thread mcpjam-inspector/server/utils/chat-ingestion.ts
Comment thread mcpjam-inspector/server/utils/chat-ingestion.ts
Comment thread mcpjam-inspector/server/utils/mcpjam-stream-handler.ts
@dosubot dosubot Bot added size:XL This PR changes 500-999 lines, ignoring generated files. and removed size:L This PR changes 100-499 lines, ignoring generated files. labels Mar 17, 2026
@chelojimenez chelojimenez merged commit b3443d5 into main Mar 17, 2026
2 of 3 checks passed
@chelojimenez chelojimenez deleted the pii-scrubbing branch March 17, 2026 08:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request size:XL This PR changes 500-999 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant