Skip to content

Move host context to the preview surface#1928

Merged
chelojimenez merged 4 commits intomainfrom
codex/move-hostcontext-out-of-connection
Apr 25, 2026
Merged

Move host context to the preview surface#1928
chelojimenez merged 4 commits intomainfrom
codex/move-hostcontext-out-of-connection

Conversation

@chelojimenez
Copy link
Copy Markdown
Contributor

@chelojimenez chelojimenez commented Apr 25, 2026

Summary

  • Split connection settings from host context so hostContext is now edited from the preview/runtime surface instead of Connection Settings.
  • Added a dedicated host-context store, dialog, and header controls, and renamed the shared preview header modules to HostContext*.
  • Updated workspace save plumbing so connection config and host context persist independently while still composing a full WorkspaceClientConfig.
  • Switched renderers and preview consumers to read host context from the new store, including theme, display mode, locale, time zone, safe area, and device capabilities.
  • Added and updated unit coverage for the new split state, save flows, and preview header behavior.

Testing

  • Unit tests updated for the new host-context store and save flow split.
  • UI/component test coverage added for the Host Context header/dialog and refreshed for playground and renderer consumers.
  • Local Vitest sweep passed for the affected client test files.

Note

Medium Risk
Moderate risk because it refactors workspace client-config persistence into two independently saved slices (connection config vs hostContext) with new pending-sync coordination, which could affect save/echo timing or overwrite behavior across workspaces.

Overview
Moves hostContext editing to the preview surface. Connection Settings (ClientConfigTab) now only edits connection defaults + client capabilities, while a new HostContextHeader + HostContextDialog provide live draft edits and persistence of raw hostContext JSON from the playground/views surfaces.

Splits state and persistence paths. Introduces useHostContextStore and useWorkspaceClientConfigSyncPending to coordinate pending remote-echo saves across both slices, and updates use-workspace-state to persist connection-config and host-context changes independently while still composing a full WorkspaceClientConfig payload for Convex.

Updates preview/widget consumers (PlaygroundMain, renderers, tool parts, safe-area editor, views/app-builder tabs) and refreshes unit tests to read from the new host-context store and cover the new save/sync behavior.

Reviewed by Cursor Bugbot for commit 97a92ef. Bugbot is set up for automated code reviews on this repo. Configure here.

@dosubot dosubot Bot added the size:XXL This PR changes 1000+ lines, ignoring generated files. label Apr 25, 2026
@chelojimenez
Copy link
Copy Markdown
Contributor Author

chelojimenez commented Apr 25, 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 Apr 25, 2026
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 25, 2026

Internal preview

Preview URL: https://mcp-inspector-pr-1928.up.railway.app
Deployed commit: a3c9342
PR head commit: 97a92ef
Backend target: staging fallback.
Health: ❌ Convex unreachable — see upsert-preview job logs (staging may need convex deploy)
Access is employee-only in non-production environments.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 25, 2026

Walkthrough

The change splits workspace configuration into separate connection-config and host-context drafts with new types and composition helpers. It adds a dedicated useHostContextStore, a HostContextHeader and HostContextDialog (removing DisplayContextHeader), and threads activeWorkspaceId/onSaveHostContext through playground and view components. Workspace hooks expose handleUpdateHostContext, a new useWorkspaceClientConfigSyncPending hook tracks per-workspace sync state, and stores, persistence, and tests are updated to reflect the draft/compose flow.


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: 3

Caution

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

⚠️ Outside diff range comments (1)
mcpjam-inspector/client/src/hooks/use-workspace-state.ts (1)

866-896: ⚠️ Potential issue | 🔴 Critical

Do not reuse one pending-sync slot for both save paths.

Line 867 clears the single pendingClientConfigSyncRef before registering the next waiter. With separate connection and host-context saves now funneled through this helper, save B can reject save A; save A's catch then calls clearPendingClientConfigSync() again and can tear down save B as well. The end state is both stores falling into failSave() even if Convex persisted the writes. Queue these per workspace, or serialize saves until the prior remote echo completes.

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

In `@mcpjam-inspector/client/src/hooks/use-workspace-state.ts` around lines 866 -
896, The current logic in use-workspace-state.ts reuses
pendingClientConfigSyncRef for all saves, so clearPendingClientConfigSync() can
cancel a different save; change this to track pending syncs per workspace (e.g.,
replace pendingClientConfigSyncRef with a Map keyed by workspaceId) or serialize
saves per workspace by awaiting an existing pending promise before creating a
new remoteEchoPromise; ensure the new structure stores the same fields
(workspaceId, expectedSerializedConfig, resolve, reject, timeoutId), update
clearPendingClientConfigSync to clear only the entry for the current
workspaceId, and keep convexUpdateClientConfig/remoteEchoPromise flow and
controller.failSave behavior but scoped so one save cannot reject another
workspace's pending save.
🧹 Nitpick comments (6)
mcpjam-inspector/client/src/hooks/use-workspace-client-config-sync-pending.ts (1)

4-17: Consider a name that reflects the dual subscription.

The hook now ORs in host-context sync as well, so the ClientConfig-only label slightly understates its scope. Something like useWorkspaceConfigSyncPending or useWorkspaceSavePending would more faithfully advertise that both connection-config and host-context echoes are gating the result. Purely cosmetic — behavior is sound.

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

In
`@mcpjam-inspector/client/src/hooks/use-workspace-client-config-sync-pending.ts`
around lines 4 - 17, The hook useWorkspaceClientConfigSyncPending now also
checks host-context sync, so rename the function to a clearer name (e.g.
useWorkspaceConfigSyncPending or useWorkspaceSavePending) and update its export
and all imports to match; ensure you update the function identifier in the
declaration and any places that import or call
useWorkspaceClientConfigSyncPending so consumers compile and behavior remains
unchanged (the internal logic using useClientConfigStore and useHostContextStore
stays the same).
mcpjam-inspector/client/src/hooks/__tests__/use-server-state.test.tsx (1)

200-212: Add one regression for host-context pending.

This setup now mirrors the split-store model, but the suite still only proves the client-config half of useWorkspaceClientConfigSyncPending. A targeted case with useHostContextStore.setState({ pendingWorkspaceId: "default", isAwaitingRemoteEcho: true }) would lock in the new branch.

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

In `@mcpjam-inspector/client/src/hooks/__tests__/use-server-state.test.tsx` around
lines 200 - 212, Add a regression test exercising the host-context pending
branch by initializing the host store with useHostContextStore.setState({
pendingWorkspaceId: "default", isAwaitingRemoteEcho: true, ...other required
keys }) and then asserting that useWorkspaceClientConfigSyncPending (or the hook
under test) reports the pending state for that workspace; locate references to
useHostContextStore, pendingWorkspaceId, isAwaitingRemoteEcho and
useWorkspaceClientConfigSyncPending in the test file and add a targeted case
mirroring the split-store model to validate the new branch.
mcpjam-inspector/client/src/components/shared/HostContextHeader.tsx (2)

150-152: Hoist session-stable fallbacks out of the render path.

navigator.language and the resolved time zone don't change for the lifetime of the tab, yet they're recomputed on every render. Lifting them to module scope (or wrapping in useMemo) is cheap polish.

♻️ Suggested tweak
-  const fallbackLocale = navigator.language || "en-US";
-  const fallbackTimeZone =
-    Intl.DateTimeFormat().resolvedOptions().timeZone || "UTC";
+  const fallbackLocale = useMemo(() => navigator.language || "en-US", []);
+  const fallbackTimeZone = useMemo(
+    () => Intl.DateTimeFormat().resolvedOptions().timeZone || "UTC",
+    [],
+  );

Or simply declare them as module-level constants outside the component.

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

In `@mcpjam-inspector/client/src/components/shared/HostContextHeader.tsx` around
lines 150 - 152, Move the per-tab constant computations out of the render path
by hoisting navigator.language and
Intl.DateTimeFormat().resolvedOptions().timeZone to module-level constants
(e.g., replace the in-component fallbackLocale and fallbackTimeZone with
module-scope constants) or memoize them once (e.g., via useMemo called at
component init); update references to fallbackLocale and fallbackTimeZone in
HostContextHeader so the values are computed only once for the lifetime of the
tab.

405-407: Tooltip backticks render literally.

The tooltip body renders the string verbatim, so users see the backticks rather than monospace-styled hostContext. Either drop them or wrap the identifier in a <code> element.

♻️ Suggested tweak
-            <p className="text-xs text-muted-foreground">
-              Edit raw `hostContext` JSON
-            </p>
+            <p className="text-xs text-muted-foreground">
+              Edit raw <code>hostContext</code> JSON
+            </p>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@mcpjam-inspector/client/src/components/shared/HostContextHeader.tsx` around
lines 405 - 407, The tooltip text currently includes backticks literally in the
JSX paragraph inside HostContextHeader (the <p className="text-xs
text-muted-foreground"> element) and should instead render the identifier as
code or plain text; update the string "Edit raw `hostContext` JSON" to either
"Edit raw hostContext JSON" or wrap the identifier in a <code> element (e.g.,
Edit raw <code>hostContext</code> JSON) so users see it styled as code rather
than showing backticks.
mcpjam-inspector/client/src/lib/client-config.ts (1)

102-102: Mark the alias deprecated to nudge callers forward.

buildDefaultHostContext exists purely as a transitional re-export. A @deprecated tag turns it into a self-extinguishing migration aid rather than a permanent twin name.

♻️ Suggested tweak
+/** `@deprecated` Use {`@link` buildDefaultWorkspaceHostContext} instead. */
 export const buildDefaultHostContext = buildDefaultWorkspaceHostContext;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@mcpjam-inspector/client/src/lib/client-config.ts` at line 102, Add a JSDoc
deprecation tag to the transitional alias export so callers are nudged to the
new name; update the export of buildDefaultHostContext (which re-exports
buildDefaultWorkspaceHostContext) by adding a /** `@deprecated` Use
buildDefaultWorkspaceHostContext instead. */ comment immediately above the
export declaration, keeping the alias but marking it deprecated and mentioning
the preferred symbol buildDefaultWorkspaceHostContext.
mcpjam-inspector/client/src/test/mocks/stores.ts (1)

336-344: Preset silently drops availableDisplayModes if overrides provide their own draftHostContext.

A test that does hostContextWithDisplayModes(["inline"], { draftHostContext: { theme: "dark" } }) will lose the availableDisplayModes it just asked for, because the override replaces the whole object. Merging keeps the preset's intent intact.

♻️ Suggested tweak
   hostContextWithDisplayModes: (
     availableDisplayModes: HostDisplayMode[],
     overrides: Partial<MockHostContextStoreState> = {},
   ) =>
     createMockHostContextStoreState({
-      draftHostContext: { availableDisplayModes },
       ...overrides,
+      draftHostContext: {
+        availableDisplayModes,
+        ...(overrides.draftHostContext ?? {}),
+      },
     }),
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@mcpjam-inspector/client/src/test/mocks/stores.ts` around lines 336 - 344, The
preset function hostContextWithDisplayModes currently passes draftHostContext: {
availableDisplayModes } and then spreads overrides which can replace
draftHostContext entirely; instead merge the passed availableDisplayModes into
any override.draftHostContext so the preset value is preserved — e.g. call
createMockHostContextStoreState with draftHostContext: {
...(overrides.draftHostContext || {}), availableDisplayModes } and spread the
remaining overrides, referencing hostContextWithDisplayModes,
createMockHostContextStoreState, draftHostContext, and availableDisplayModes to
locate the change.
🤖 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/components/client-config/ClientConfigTab.tsx`:
- Around line 178-187: The per-section "Reset" Button currently only checks
isDirty but still allows clicks during save/sync; update the Reset buttons to
include the full lock condition (disabled={isSaving || syncPending || !isDirty})
so they are disabled while isSaving or syncPending are true (same as the main
Save button), and ensure any per-section reset handlers (the Reset Button
instances in ClientConfigTab.tsx and the similar buttons referenced around the
212-219 and 232 locations) respect that disabled state so they cannot mutate
draft state during remote-echo wait.

In `@mcpjam-inspector/client/src/components/evals/use-eval-trace-tool-context.ts`:
- Around line 80-82: The sync-pending check currently always uses
appState.activeWorkspaceId, which is wrong for callers that pass a different
workspace; update the call to use the effectiveHostedWorkspaceId variable
instead so useWorkspaceClientConfigSyncPending(effectiveHostedWorkspaceId)
checks the correct store entry (locate the call site near
useWorkspaceClientConfigSyncPending and replace appState.activeWorkspaceId with
effectiveHostedWorkspaceId).

In `@mcpjam-inspector/client/src/hooks/use-workspace-state.ts`:
- Around line 926-952: The two resolvers resolvePersistedConnectionConfig and
resolvePersistedHostContext currently pick only saved* and default* values;
update them to prefer any workspace-specific pending saves first: check
clientConfigStore.pendingSavedConfig (and
hostContextStore.pendingSavedHostContext) for an entry for the given workspaceId
and return that if present before falling back to savedConfig/defaultConfig or
savedHostContext/defaultHostContext; ensure you reference the workspaceId-scoped
pending slice (not a global pending) so pending in-flight changes are composed
into composeWorkspaceClientConfig / pickWorkspaceHostContext rather than being
overwritten.

---

Outside diff comments:
In `@mcpjam-inspector/client/src/hooks/use-workspace-state.ts`:
- Around line 866-896: The current logic in use-workspace-state.ts reuses
pendingClientConfigSyncRef for all saves, so clearPendingClientConfigSync() can
cancel a different save; change this to track pending syncs per workspace (e.g.,
replace pendingClientConfigSyncRef with a Map keyed by workspaceId) or serialize
saves per workspace by awaiting an existing pending promise before creating a
new remoteEchoPromise; ensure the new structure stores the same fields
(workspaceId, expectedSerializedConfig, resolve, reject, timeoutId), update
clearPendingClientConfigSync to clear only the entry for the current
workspaceId, and keep convexUpdateClientConfig/remoteEchoPromise flow and
controller.failSave behavior but scoped so one save cannot reject another
workspace's pending save.

---

Nitpick comments:
In `@mcpjam-inspector/client/src/components/shared/HostContextHeader.tsx`:
- Around line 150-152: Move the per-tab constant computations out of the render
path by hoisting navigator.language and
Intl.DateTimeFormat().resolvedOptions().timeZone to module-level constants
(e.g., replace the in-component fallbackLocale and fallbackTimeZone with
module-scope constants) or memoize them once (e.g., via useMemo called at
component init); update references to fallbackLocale and fallbackTimeZone in
HostContextHeader so the values are computed only once for the lifetime of the
tab.
- Around line 405-407: The tooltip text currently includes backticks literally
in the JSX paragraph inside HostContextHeader (the <p className="text-xs
text-muted-foreground"> element) and should instead render the identifier as
code or plain text; update the string "Edit raw `hostContext` JSON" to either
"Edit raw hostContext JSON" or wrap the identifier in a <code> element (e.g.,
Edit raw <code>hostContext</code> JSON) so users see it styled as code rather
than showing backticks.

In `@mcpjam-inspector/client/src/hooks/__tests__/use-server-state.test.tsx`:
- Around line 200-212: Add a regression test exercising the host-context pending
branch by initializing the host store with useHostContextStore.setState({
pendingWorkspaceId: "default", isAwaitingRemoteEcho: true, ...other required
keys }) and then asserting that useWorkspaceClientConfigSyncPending (or the hook
under test) reports the pending state for that workspace; locate references to
useHostContextStore, pendingWorkspaceId, isAwaitingRemoteEcho and
useWorkspaceClientConfigSyncPending in the test file and add a targeted case
mirroring the split-store model to validate the new branch.

In
`@mcpjam-inspector/client/src/hooks/use-workspace-client-config-sync-pending.ts`:
- Around line 4-17: The hook useWorkspaceClientConfigSyncPending now also checks
host-context sync, so rename the function to a clearer name (e.g.
useWorkspaceConfigSyncPending or useWorkspaceSavePending) and update its export
and all imports to match; ensure you update the function identifier in the
declaration and any places that import or call
useWorkspaceClientConfigSyncPending so consumers compile and behavior remains
unchanged (the internal logic using useClientConfigStore and useHostContextStore
stays the same).

In `@mcpjam-inspector/client/src/lib/client-config.ts`:
- Line 102: Add a JSDoc deprecation tag to the transitional alias export so
callers are nudged to the new name; update the export of buildDefaultHostContext
(which re-exports buildDefaultWorkspaceHostContext) by adding a /** `@deprecated`
Use buildDefaultWorkspaceHostContext instead. */ comment immediately above the
export declaration, keeping the alias but marking it deprecated and mentioning
the preferred symbol buildDefaultWorkspaceHostContext.

In `@mcpjam-inspector/client/src/test/mocks/stores.ts`:
- Around line 336-344: The preset function hostContextWithDisplayModes currently
passes draftHostContext: { availableDisplayModes } and then spreads overrides
which can replace draftHostContext entirely; instead merge the passed
availableDisplayModes into any override.draftHostContext so the preset value is
preserved — e.g. call createMockHostContextStoreState with draftHostContext: {
...(overrides.draftHostContext || {}), availableDisplayModes } and spread the
remaining overrides, referencing hostContextWithDisplayModes,
createMockHostContextStoreState, draftHostContext, and availableDisplayModes to
locate the change.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 144111b4-615c-42b0-b3ee-e13c1e4f8a17

📥 Commits

Reviewing files that changed from the base of the PR and between a50f25e and 843677d.

📒 Files selected for processing (35)
  • mcpjam-inspector/client/src/App.tsx
  • mcpjam-inspector/client/src/components/ServersTab.tsx
  • mcpjam-inspector/client/src/components/ViewsTab.tsx
  • mcpjam-inspector/client/src/components/chat-v2/thread/__tests__/chatgpt-app-renderer.test.tsx
  • mcpjam-inspector/client/src/components/chat-v2/thread/chatgpt-app-renderer.tsx
  • mcpjam-inspector/client/src/components/chat-v2/thread/mcp-apps/__tests__/mcp-apps-renderer.test.tsx
  • mcpjam-inspector/client/src/components/chat-v2/thread/mcp-apps/mcp-apps-renderer.tsx
  • mcpjam-inspector/client/src/components/chat-v2/thread/parts/__tests__/display-modes.test.tsx
  • mcpjam-inspector/client/src/components/chat-v2/thread/parts/tool-part.tsx
  • mcpjam-inspector/client/src/components/client-config/ClientConfigTab.tsx
  • mcpjam-inspector/client/src/components/client-config/WorkspaceClientConfigSync.tsx
  • mcpjam-inspector/client/src/components/client-config/__tests__/ClientConfigTab.test.tsx
  • mcpjam-inspector/client/src/components/client-config/__tests__/WorkspaceClientConfigSync.test.tsx
  • mcpjam-inspector/client/src/components/evals/use-eval-trace-tool-context.ts
  • mcpjam-inspector/client/src/components/shared/DisplayContextHeader.tsx
  • mcpjam-inspector/client/src/components/shared/HostContextDialog.tsx
  • mcpjam-inspector/client/src/components/shared/HostContextHeader.tsx
  • mcpjam-inspector/client/src/components/shared/__tests__/HostContextHeader.test.tsx
  • mcpjam-inspector/client/src/components/shared/host-context-constants.ts
  • mcpjam-inspector/client/src/components/shared/host-context-picker-bodies.tsx
  • mcpjam-inspector/client/src/components/ui-playground/AppBuilderTab.tsx
  • mcpjam-inspector/client/src/components/ui-playground/PlaygroundMain.tsx
  • mcpjam-inspector/client/src/components/ui-playground/SafeAreaEditor.tsx
  • mcpjam-inspector/client/src/components/ui-playground/__tests__/PlaygroundMain.test.tsx
  • mcpjam-inspector/client/src/hooks/__tests__/use-app-state.test.tsx
  • mcpjam-inspector/client/src/hooks/__tests__/use-server-state.test.tsx
  • mcpjam-inspector/client/src/hooks/__tests__/use-workspace-state.test.tsx
  • mcpjam-inspector/client/src/hooks/use-app-state.ts
  • mcpjam-inspector/client/src/hooks/use-server-state.ts
  • mcpjam-inspector/client/src/hooks/use-workspace-client-config-sync-pending.ts
  • mcpjam-inspector/client/src/hooks/use-workspace-state.ts
  • mcpjam-inspector/client/src/lib/client-config.ts
  • mcpjam-inspector/client/src/stores/client-config-store.ts
  • mcpjam-inspector/client/src/stores/host-context-store.ts
  • mcpjam-inspector/client/src/test/mocks/stores.ts
💤 Files with no reviewable changes (1)
  • mcpjam-inspector/client/src/components/shared/DisplayContextHeader.tsx

Comment on lines +178 to 187
disabled={isSaving || syncPending || !isDirty}
className="h-7 text-xs"
>
Reset
</Button>
<Button
size="sm"
onClick={handleSave}
disabled={isSaving || !isDirty}
disabled={isSaving || syncPending || !isDirty}
className="h-7 text-xs"
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

Disable section-level Reset while save/sync lock is active

The per-section Reset button still mutates state while the rest of the editor is locked (isSaving || syncPending). That can alter drafts during remote-echo wait and undermine the sync guard.

Suggested patch
                 <button
                   type="button"
                   className="flex items-center gap-1 text-[11px] text-muted-foreground transition-colors hover:text-foreground"
                   onClick={() => resetSectionToDefault(section.key)}
+                  disabled={isSaving || syncPending}
                 >
                   <RotateCcw className="h-2.5 w-2.5" />
                   Reset
                 </button>

Also applies to: 212-219, 232-232

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

In `@mcpjam-inspector/client/src/components/client-config/ClientConfigTab.tsx`
around lines 178 - 187, The per-section "Reset" Button currently only checks
isDirty but still allows clicks during save/sync; update the Reset buttons to
include the full lock condition (disabled={isSaving || syncPending || !isDirty})
so they are disabled while isSaving or syncPending are true (same as the main
Save button), and ensure any per-section reset handlers (the Reset Button
instances in ClientConfigTab.tsx and the similar buttons referenced around the
212-219 and 232 locations) respect that disabled state so they cannot mutate
draft state during remote-echo wait.

Comment on lines +80 to 82
const isClientConfigSyncPending = useWorkspaceClientConfigSyncPending(
appState.activeWorkspaceId,
);
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 | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Call sites for useEvalTraceToolContext:"
rg -n -C4 '\buseEvalTraceToolContext\s*\(' --type ts --type tsx

echo
echo "Nearby workspaceId/sharedWorkspaceId usage:"
rg -n -C3 '\bworkspaceId\b|\bsharedWorkspaceId\b|\bactiveWorkspaceId\b' --type ts --type tsx

Repository: MCPJam/inspector

Length of output: 146


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== Call sites for useEvalTraceToolContext ==="
rg -n -C4 'useEvalTraceToolContext\s*\(' --type ts

echo ""
echo "=== Hook parameter definition ==="
rg -n -B5 -A15 'function useEvalTraceToolContext|const useEvalTraceToolContext' --type ts

echo ""
echo "=== isClientConfigSyncPending usage within context hook ==="
rg -n -B3 -A3 'isClientConfigSyncPending' mcpjam-inspector/client/src/components/evals/

echo ""
echo "=== useWorkspaceClientConfigSyncPending hook implementation ==="
cat mcpjam-inspector/client/src/hooks/use-workspace-client-config-sync-pending.ts 2>/dev/null | head -25

Repository: MCPJam/inspector

Length of output: 10079


Pass the workspace parameter to the sync-pending check.

Lines 80-82 always check sync status for appState.activeWorkspaceId, but the hook accepts an optional workspaceId parameter that can differ from the active workspace (as shown in test cases at line 129). When a caller requests context for a non-active workspace, this pending gate will wait on the wrong store entry. Change to:

const isClientConfigSyncPending = useWorkspaceClientConfigSyncPending(
  effectiveHostedWorkspaceId,
);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@mcpjam-inspector/client/src/components/evals/use-eval-trace-tool-context.ts`
around lines 80 - 82, The sync-pending check currently always uses
appState.activeWorkspaceId, which is wrong for callers that pass a different
workspace; update the call to use the effectiveHostedWorkspaceId variable
instead so useWorkspaceClientConfigSyncPending(effectiveHostedWorkspaceId)
checks the correct store entry (locate the call site near
useWorkspaceClientConfigSyncPending and replace appState.activeWorkspaceId with
effectiveHostedWorkspaceId).

Comment thread mcpjam-inspector/client/src/hooks/use-workspace-state.ts
Comment thread mcpjam-inspector/client/src/hooks/use-workspace-state.ts
Comment thread mcpjam-inspector/client/src/hooks/use-workspace-state.ts
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.

Caution

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

⚠️ Outside diff range comments (1)
mcpjam-inspector/client/src/hooks/__tests__/use-server-state.test.tsx (1)

1151-1156: ⚠️ Potential issue | 🟡 Minor

Add store resets to beforeEach in the OAuth callback in-flight dispatch suite.

All three sibling suites reset useClientConfigStore and useHostContextStore in their beforeEach, but this one omits them. Since Zustand stores are module-level singletons, omitting resets risks state bleed (e.g., stray pendingWorkspaceId or isAwaitingRemoteEcho: true from prior tests) causing flakes when run in isolation or as part of the full suite. Mirror the store resets used above:

Example reset pattern from sibling suites
useClientConfigStore.setState({
  activeWorkspaceId: null,
  defaultConfig: null,
  savedConfig: undefined,
  draftConfig: null,
  connectionDefaultsText: "{}",
  isAwaitingRemoteEcho: false,
});
useHostContextStore.setState({
  activeWorkspaceId: null,
  defaultHostContext: {},
});
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@mcpjam-inspector/client/src/hooks/__tests__/use-server-state.test.tsx` around
lines 1151 - 1156, The OAuth callback in-flight dispatch test suite's beforeEach
is missing resets for the Zustand stores, which can cause state bleed; update
the beforeEach inside the "useServerState OAuth callback in-flight dispatch"
describe to call useClientConfigStore.setState(...) to reset activeWorkspaceId,
defaultConfig, savedConfig, draftConfig, connectionDefaultsText to "{}" and
isAwaitingRemoteEcho to false, and call useHostContextStore.setState(...) to
reset activeWorkspaceId to null and defaultHostContext to {} so the store state
matches the sibling suites.
🧹 Nitpick comments (2)
mcpjam-inspector/client/src/components/ui-playground/PlaygroundMain.tsx (1)

162-171: Silent no-op callbacks risk misleading upstream callers.

onDeviceTypeChange, onLocaleChange, and onTimeZoneChange (and their corresponding value props) remain in PlaygroundMainProps but are now destructured into _-prefixed bindings and never invoked — HostContextHeader mutates the store directly, so any parent still subscribing to these callbacks will silently stop receiving notifications. The "kept for backward compatibility" comment is accurate about the values, but the callbacks are a behavior change disguised as a no-op.

Two reasonable paths:

  • Remove the now-defunct fields from the public interface so callers get a TypeScript error and can migrate to the host-context store, or
  • Subscribe to the host-context store inside PlaygroundMain and bridge changes back through these callbacks until the next breaking-change window.

Worth at least a brief deprecation note in the prop docs if neither is in scope for this PR.

Also applies to: 230-240

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

In `@mcpjam-inspector/client/src/components/ui-playground/PlaygroundMain.tsx`
around lines 162 - 171, PlaygroundMainProps currently keeps onDeviceTypeChange,
onLocaleChange, and onTimeZoneChange but PlaygroundMain destructures them into
_-prefixed variables and never calls them (HostContextHeader mutates the host
store), causing silent no-op behavior; either remove these callback props from
the public PlaygroundMainProps interface (and add a deprecation note in the prop
docs) so callers get a TypeScript error to migrate to the host-context store, or
implement a bridging subscription inside PlaygroundMain (e.g., in the
PlaygroundMain component) that listens to the HostContextHeader/host-context
store and invokes onDeviceTypeChange, onLocaleChange, and onTimeZoneChange when
the corresponding values change; update references to PlaygroundMainProps,
PlaygroundMain, and HostContextHeader accordingly and apply the same change for
the other occurrence noted (around the second block at lines 230-240).
mcpjam-inspector/client/src/hooks/__tests__/use-server-state.test.tsx (1)

185-212: A trio of identical seedings begs for a helper.

The three beforeEach blocks (lines 185–212, 876–903, 1028–1055) reset useClientConfigStore and useHostContextStore to byte-for-byte identical baselines. Hoisting these into a small resetConfigStores() helper would shrink the file, eliminate three places to update when either store gains a field, and make the intent of each suite easier to read.

♻️ Sketch of the extraction
+function resetConfigStores() {
+  useClientConfigStore.setState({
+    activeWorkspaceId: null,
+    defaultConfig: null,
+    savedConfig: undefined,
+    draftConfig: null,
+    connectionDefaultsText: "{}",
+    clientCapabilitiesText: "{}",
+    clientCapabilitiesError: null,
+    connectionDefaultsError: null,
+    isSaving: false,
+    isDirty: false,
+    pendingWorkspaceId: null,
+    pendingSavedConfig: undefined,
+    isAwaitingRemoteEcho: false,
+  });
+  useHostContextStore.setState({
+    activeWorkspaceId: null,
+    defaultHostContext: {},
+    savedHostContext: undefined,
+    draftHostContext: {},
+    hostContextText: "{}",
+    hostContextError: null,
+    isSaving: false,
+    isDirty: false,
+    pendingWorkspaceId: null,
+    pendingSavedHostContext: undefined,
+    isAwaitingRemoteEcho: false,
+  });
+}

Then each beforeEach simplifies to a single resetConfigStores(); call.

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

In `@mcpjam-inspector/client/src/hooks/__tests__/use-server-state.test.tsx` around
lines 185 - 212, The three identical beforeEach seedings repeatedly call
useClientConfigStore.setState(...) and useHostContextStore.setState(...) with
the same baseline; extract this into a single helper function (e.g.,
resetConfigStores) that encapsulates those two setState calls and any shared
default values, then replace each beforeEach block with a call to
resetConfigStores(); ensure the helper initializes the same fields shown
(activeWorkspaceId, defaultConfig, savedConfig, draftConfig,
connectionDefaultsText, clientCapabilitiesText, clientCapabilitiesError,
connectionDefaultsError, isSaving, isDirty, pendingWorkspaceId,
pendingSavedConfig, isAwaitingRemoteEcho and the host equivalents) so tests
remain behaviorally identical.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@mcpjam-inspector/client/src/hooks/__tests__/use-server-state.test.tsx`:
- Around line 1151-1156: The OAuth callback in-flight dispatch test suite's
beforeEach is missing resets for the Zustand stores, which can cause state
bleed; update the beforeEach inside the "useServerState OAuth callback in-flight
dispatch" describe to call useClientConfigStore.setState(...) to reset
activeWorkspaceId, defaultConfig, savedConfig, draftConfig,
connectionDefaultsText to "{}" and isAwaitingRemoteEcho to false, and call
useHostContextStore.setState(...) to reset activeWorkspaceId to null and
defaultHostContext to {} so the store state matches the sibling suites.

---

Nitpick comments:
In `@mcpjam-inspector/client/src/components/ui-playground/PlaygroundMain.tsx`:
- Around line 162-171: PlaygroundMainProps currently keeps onDeviceTypeChange,
onLocaleChange, and onTimeZoneChange but PlaygroundMain destructures them into
_-prefixed variables and never calls them (HostContextHeader mutates the host
store), causing silent no-op behavior; either remove these callback props from
the public PlaygroundMainProps interface (and add a deprecation note in the prop
docs) so callers get a TypeScript error to migrate to the host-context store, or
implement a bridging subscription inside PlaygroundMain (e.g., in the
PlaygroundMain component) that listens to the HostContextHeader/host-context
store and invokes onDeviceTypeChange, onLocaleChange, and onTimeZoneChange when
the corresponding values change; update references to PlaygroundMainProps,
PlaygroundMain, and HostContextHeader accordingly and apply the same change for
the other occurrence noted (around the second block at lines 230-240).

In `@mcpjam-inspector/client/src/hooks/__tests__/use-server-state.test.tsx`:
- Around line 185-212: The three identical beforeEach seedings repeatedly call
useClientConfigStore.setState(...) and useHostContextStore.setState(...) with
the same baseline; extract this into a single helper function (e.g.,
resetConfigStores) that encapsulates those two setState calls and any shared
default values, then replace each beforeEach block with a call to
resetConfigStores(); ensure the helper initializes the same fields shown
(activeWorkspaceId, defaultConfig, savedConfig, draftConfig,
connectionDefaultsText, clientCapabilitiesText, clientCapabilitiesError,
connectionDefaultsError, isSaving, isDirty, pendingWorkspaceId,
pendingSavedConfig, isAwaitingRemoteEcho and the host equivalents) so tests
remain behaviorally identical.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 07dd9d98-889e-4162-8974-35519fd99002

📥 Commits

Reviewing files that changed from the base of the PR and between 843677d and 5e04e5a.

📒 Files selected for processing (6)
  • mcpjam-inspector/client/src/components/chat-v2/thread/mcp-apps/mcp-apps-renderer.tsx
  • mcpjam-inspector/client/src/components/ui-playground/PlaygroundMain.tsx
  • mcpjam-inspector/client/src/hooks/__tests__/use-app-state.test.tsx
  • mcpjam-inspector/client/src/hooks/__tests__/use-server-state.test.tsx
  • mcpjam-inspector/client/src/hooks/use-app-state.ts
  • mcpjam-inspector/client/src/hooks/use-server-state.ts
✅ Files skipped from review due to trivial changes (1)
  • mcpjam-inspector/client/src/hooks/tests/use-app-state.test.tsx
🚧 Files skipped from review as they are similar to previous changes (2)
  • mcpjam-inspector/client/src/hooks/use-app-state.ts
  • mcpjam-inspector/client/src/hooks/use-server-state.ts

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5e04e5af96

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +932 to +934
clientConfigStore.savedConfig ??
clientConfigStore.defaultConfig ??
pickWorkspaceConnectionConfig(workspaceClientConfig)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Scope fallback connection config to the target workspace

resolvePersistedConnectionConfig always prefers clientConfigStore.savedConfig/defaultConfig without checking which workspace those values belong to. During a workspace switch (or any call where workspaceId differs from the store’s activeWorkspaceId), handleUpdateHostContext can compose and save the new workspace’s clientConfig with the previous workspace’s connection defaults/capabilities, overwriting the target workspace with stale data. This helper should only reuse store values when activeWorkspaceId === workspaceId, otherwise fall back to the target workspace’s persisted config.

Useful? React with 👍 / 👎.

…t-out-of-connection

# Conflicts:
#	mcpjam-inspector/client/src/components/chat-v2/thread/mcp-apps/__tests__/mcp-apps-renderer.test.tsx
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.

♻️ Duplicate comments (1)
mcpjam-inspector/client/src/hooks/use-workspace-state.ts (1)

1016-1046: ⚠️ Potential issue | 🔴 Critical

Cross-slice resolvers can still re-emit a stale sibling during an in-flight save.

The supersession added in persistWorkspaceClientConfig only short-circuits the local sync tracker; it does not prevent a sibling save from composing a stale slice into the actual backend write. Consider this sequence on a single workspace:

  1. handleUpdateClientConfig begins → store sets pendingSavedConfig = newConn (and savedConfig is still old until echo).
  2. Before the echo arrives, handleUpdateHostContext fires.
  3. The new save calls resolvePersistedConnectionConfig(ws) → returns clientConfigStore.savedConfig ?? defaultConfig (old) since pendingSavedConfig is ignored.
  4. composeWorkspaceClientConfig({ connectionConfig: oldConn, hostContext: newHC, fallback: workspaceClientConfig }) — fallback is also old (echo hadn't arrived) and is preempted by the explicit connectionConfig.
  5. convexUpdateClientConfig writes { oldConn, newHC }, clobbering newConn server-side. The reverse path (host-context in-flight, connection save begins) has the same shape.

Prefer the workspace-scoped pending slice before falling back to saved*/default* so the in-flight sibling is preserved through the compose.

🛡️ Suggested direction
   const resolvePersistedConnectionConfig = useCallback(
     (workspaceId: string): WorkspaceConnectionConfigDraft => {
       const clientConfigStore = useClientConfigStore.getState();
       const workspaceClientConfig = effectiveWorkspaces[workspaceId]?.clientConfig;
       const scopedStoreConfig = doesStoreSliceBelongToWorkspace(
         clientConfigStore.activeWorkspaceId,
         workspaceId,
       )
-        ? clientConfigStore.savedConfig ?? clientConfigStore.defaultConfig
+        ? (clientConfigStore.isAwaitingRemoteEcho &&
+           clientConfigStore.pendingWorkspaceId === workspaceId
+            ? clientConfigStore.pendingSavedConfig
+            : undefined) ??
+          clientConfigStore.savedConfig ??
+          clientConfigStore.defaultConfig
         : undefined;

       return scopedStoreConfig ?? pickWorkspaceConnectionConfig(workspaceClientConfig);
     },
     [effectiveWorkspaces],
   );

   const resolvePersistedHostContext = useCallback(
     (workspaceId: string): WorkspaceHostContextDraft => {
       const hostContextStore = useHostContextStore.getState();
       const workspaceClientConfig = effectiveWorkspaces[workspaceId]?.clientConfig;
       const scopedStoreHostContext = doesStoreSliceBelongToWorkspace(
         hostContextStore.activeWorkspaceId,
         workspaceId,
       )
-        ? hostContextStore.savedHostContext ?? hostContextStore.defaultHostContext
+        ? (hostContextStore.isAwaitingRemoteEcho &&
+           hostContextStore.pendingWorkspaceId === workspaceId
+            ? hostContextStore.pendingSavedHostContext
+            : undefined) ??
+          hostContextStore.savedHostContext ??
+          hostContextStore.defaultHostContext
         : undefined;

       return scopedStoreHostContext ?? pickWorkspaceHostContext(workspaceClientConfig);
     },
     [effectiveWorkspaces],
   );

A regression test where two saves on the same workspace overlap (one connection, one host-context, with the second beginning before the first’s echo) would catch this — the existing two-save coverage uses different workspaceIds, which avoids the cross-slice path entirely.

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

In `@mcpjam-inspector/client/src/hooks/use-workspace-state.ts` around lines 1016 -
1046, The resolvers resolvePersistedConnectionConfig and
resolvePersistedHostContext currently ignore in-flight pending slices (e.g.,
pendingSavedConfig/pendingSavedHostContext) and fall back to saved*/default*
which lets a concurrent sibling save recompose stale data; update both resolvers
to first check the workspace-scoped pending slice from the respective stores
(clientConfigStore.pendingSavedConfig or
hostContextStore.pendingSavedHostContext) and return that when it belongs to the
workspace, before falling back to savedConfig/savedHostContext or
defaultConfig/defaultHostContext so in-flight updates from
handleUpdateClientConfig/handleUpdateHostContext are preserved during
composeWorkspaceClientConfig/convexUpdateClientConfig.
🧹 Nitpick comments (2)
mcpjam-inspector/client/src/hooks/use-workspace-state.ts (1)

942-965: Inline timeout teardown duplicates clearPendingClientConfigSync.

The setTimeout body re-implements the same map cleanup that clearPendingClientConfigSync already encapsulates, drifting from the single source of truth for pending teardown. Delegating keeps the two cleanup paths aligned (the clearTimeout on a fired timer is a harmless no-op).

♻️ Proposed consolidation
-          const timeoutId = setTimeout(() => {
-            pendingClientConfigSyncRef.current.delete(pendingSyncId);
-            if (
-              pendingClientConfigSyncByWorkspaceRef.current.get(workspaceId) ===
-              pendingSyncId
-            ) {
-              pendingClientConfigSyncByWorkspaceRef.current.delete(workspaceId);
-            }
-            reject(new Error(WORKSPACE_CLIENT_CONFIG_SYNC_TIMEOUT_ERROR_MESSAGE));
-          }, CLIENT_CONFIG_SYNC_ECHO_TIMEOUT_MS);
+          const timeoutId = setTimeout(() => {
+            clearPendingClientConfigSync(
+              pendingSyncId,
+              new Error(WORKSPACE_CLIENT_CONFIG_SYNC_TIMEOUT_ERROR_MESSAGE),
+            );
+          }, CLIENT_CONFIG_SYNC_ECHO_TIMEOUT_MS);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@mcpjam-inspector/client/src/hooks/use-workspace-state.ts` around lines 942 -
965, The timeout callback duplicates map cleanup logic already in
clearPendingClientConfigSync; replace the manual deletion inside the setTimeout
handler with a single call to clearPendingClientConfigSync(pendingSyncId) and
then call reject(new Error(WORKSPACE_CLIENT_CONFIG_SYNC_TIMEOUT_ERROR_MESSAGE));
ensure pendingClientConfigSyncRef and pendingClientConfigSyncByWorkspaceRef are
still populated as before and that you pass the same pendingSyncId to
clearPendingClientConfigSync so the existing teardown (including clearTimeout)
remains the single source of truth.
mcpjam-inspector/client/src/hooks/__tests__/use-workspace-state.test.tsx (1)

680-1022: Tests cover the cross-workspace path but leave the same-workspace sibling-save case untested.

The new compose tests ("composes host-context saves...", "composes connection-config saves...") deliberately set useClientConfigStore/useHostContextStore activeWorkspaceId to a different workspace than the save target, so doesStoreSliceBelongToWorkspace returns false and the resolvers fall through to pickWorkspace* from the workspace clientConfig. Likewise, "keeps a newer workspace save pending when an older save times out" uses two distinct workspace ids. None of these exercise the path where a connection save and a host-context save overlap on the same workspace — the scenario flagged on the hook’s resolvers. Worth adding a regression test there once that path is settled.

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

In `@mcpjam-inspector/client/src/hooks/__tests__/use-workspace-state.test.tsx`
around lines 680 - 1022, Add a new test that exercises the same-workspace
overlap path: set useClientConfigStore and useHostContextStore activeWorkspaceId
to the same workspace id as the save target, then call
result.current.handleUpdateClientConfig(...) and
result.current.handleUpdateHostContext(...) for that same workspace to simulate
overlapping saves; verify the resolvers take the
"doesStoreSliceBelongToWorkspace" true branch by asserting
updateClientConfigMock was called with composed clientConfig (or that
pendingWorkspaceId/isAwaitingRemoteEcho behave as expected) and that the
promises resolve/reject according to the timeout behavior (use vi.useFakeTimers
and vi.advanceTimersByTimeAsync as in existing tests). Ensure you reference the
existing helper functions createAppState, renderUseWorkspaceState and the
methods handleUpdateClientConfig and handleUpdateHostContext when adding the
test.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@mcpjam-inspector/client/src/hooks/use-workspace-state.ts`:
- Around line 1016-1046: The resolvers resolvePersistedConnectionConfig and
resolvePersistedHostContext currently ignore in-flight pending slices (e.g.,
pendingSavedConfig/pendingSavedHostContext) and fall back to saved*/default*
which lets a concurrent sibling save recompose stale data; update both resolvers
to first check the workspace-scoped pending slice from the respective stores
(clientConfigStore.pendingSavedConfig or
hostContextStore.pendingSavedHostContext) and return that when it belongs to the
workspace, before falling back to savedConfig/savedHostContext or
defaultConfig/defaultHostContext so in-flight updates from
handleUpdateClientConfig/handleUpdateHostContext are preserved during
composeWorkspaceClientConfig/convexUpdateClientConfig.

---

Nitpick comments:
In `@mcpjam-inspector/client/src/hooks/__tests__/use-workspace-state.test.tsx`:
- Around line 680-1022: Add a new test that exercises the same-workspace overlap
path: set useClientConfigStore and useHostContextStore activeWorkspaceId to the
same workspace id as the save target, then call
result.current.handleUpdateClientConfig(...) and
result.current.handleUpdateHostContext(...) for that same workspace to simulate
overlapping saves; verify the resolvers take the
"doesStoreSliceBelongToWorkspace" true branch by asserting
updateClientConfigMock was called with composed clientConfig (or that
pendingWorkspaceId/isAwaitingRemoteEcho behave as expected) and that the
promises resolve/reject according to the timeout behavior (use vi.useFakeTimers
and vi.advanceTimersByTimeAsync as in existing tests). Ensure you reference the
existing helper functions createAppState, renderUseWorkspaceState and the
methods handleUpdateClientConfig and handleUpdateHostContext when adding the
test.

In `@mcpjam-inspector/client/src/hooks/use-workspace-state.ts`:
- Around line 942-965: The timeout callback duplicates map cleanup logic already
in clearPendingClientConfigSync; replace the manual deletion inside the
setTimeout handler with a single call to
clearPendingClientConfigSync(pendingSyncId) and then call reject(new
Error(WORKSPACE_CLIENT_CONFIG_SYNC_TIMEOUT_ERROR_MESSAGE)); ensure
pendingClientConfigSyncRef and pendingClientConfigSyncByWorkspaceRef are still
populated as before and that you pass the same pendingSyncId to
clearPendingClientConfigSync so the existing teardown (including clearTimeout)
remains the single source of truth.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 7f36d261-a4ee-461f-8b17-131266519f39

📥 Commits

Reviewing files that changed from the base of the PR and between 5e04e5a and 97a92ef.

📒 Files selected for processing (4)
  • mcpjam-inspector/client/src/components/chat-v2/thread/mcp-apps/__tests__/mcp-apps-renderer.test.tsx
  • mcpjam-inspector/client/src/components/chat-v2/thread/mcp-apps/mcp-apps-renderer.tsx
  • mcpjam-inspector/client/src/hooks/__tests__/use-workspace-state.test.tsx
  • mcpjam-inspector/client/src/hooks/use-workspace-state.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • mcpjam-inspector/client/src/components/chat-v2/thread/mcp-apps/mcp-apps-renderer.tsx

@chelojimenez chelojimenez merged commit 4c20534 into main Apr 25, 2026
12 checks passed
@chelojimenez chelojimenez deleted the codex/move-hostcontext-out-of-connection branch April 25, 2026 23:26
Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 97a92ef. Configure here.

useLocalFallback,
convexUpdateClientConfig,
clearPendingClientConfigSync,
convexUpdateClientConfig,
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Concurrent saves can lose data via shared pending tracker

Medium Severity

Both handleUpdateClientConfig and handleUpdateHostContext call persistWorkspaceClientConfig, which shares pendingClientConfigSyncByWorkspaceRef keyed by workspace ID. If both are called for the same workspace before the first echo returns, the second call supersedes the first via clearPendingClientConfigSync. Each call composes the full WorkspaceClientConfig by reading the saved (not in-flight) slice from the other store via resolvePersistedConnectionConfig/resolvePersistedHostContext, so the second mutation overwrites the first's changes at the Convex layer. The UI syncPending guards mitigate this today, but a future programmatic caller could trigger data loss.

Additional Locations (2)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 97a92ef. Configure here.

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:XXL This PR changes 1000+ lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant