Skip to content

🔌 fix: Preserve Ephemeral MCP Selections Across Model Switches - #13697

Merged
danny-avila merged 1 commit into
devfrom
fix/ephemeral-mcp-model-switch
Jun 11, 2026
Merged

danny-avila merged 1 commit into
devfrom
fix/ephemeral-mcp-model-switch

Conversation

@danny-avila

Copy link
Copy Markdown
Collaborator

Summary

I fixed a regression where switching the provider/model mid-conversation (or on a new chat draft) silently dropped the ephemeral MCP selection — and tool toggles — from subsequent request payloads, while the MCP badge kept displaying the selection as active. Regenerating or sending after a switch ran without MCP tools.

Root cause: the no-spec branch of useApplyModelSpecEffects (introduced in #11796) reset ephemeralAgentByConvoId to null on every newConversation call when model specs are configured. That reset is paired with a BadgeRowContext localStorage refill — but the refill effect only re-runs when the storage suffix or active spec changes, so on in-place modular switches (same conversation id, non-spec → non-spec) nothing restored the state and submissions read a null ephemeral agent.

  • Gated the no-spec reset to context transitions only — leaving a spec or moving to a different conversation key — which are exactly the cases where a BadgeRowContext refill is guaranteed to follow; in-place switches now preserve the ephemeral agent.
  • Passed prevSpecName/prevConvoId into applyModelSpecEffects from newConversation via a snapshot read of the pre-switch conversation.
  • Preserved the intended 🗂️ refactor: Artifacts via Model Specs & Scope Badge Persistence by Spec Context #11796 behaviors: starting a fresh chat from an existing conversation still refills from __defaults__, and leaving a spec still discards spec-applied values.
  • Added jest coverage for all five branches of the no-spec path (in-place existing convo, in-place new draft, spec → non-spec, convo → new chat, no specs configured).
  • Added an e2e spec asserting ephemeralAgent.mcp stays in the chat request payload after a model switch on a new chat and after regenerate on a switched conversation — verified both tests fail on the unfixed build and pass with the fix.
  • Added a non-spec "Mock Provider D" endpoint to the e2e config so tests can switch between two real ephemeral endpoints, and widened the MockEndpoint helper type accordingly.

Change Type

  • Bug fix (non-breaking change which fixes an issue)

Testing

Reproduced the original report first: new chat with an MCP server selected → first payload includes ephemeralAgent.mcp → switch provider/model → regenerate → payload omits ephemeralAgent entirely while the MCP select still shows the server. Also confirmed the new-chat variant (switch before the first send) drops it the same way.

  • cd client && npx jest useApplyModelSpecAgents useMCPSelect useToolToggle agents.spec — 67 tests pass.
  • npx playwright test --config=e2e/playwright.config.mock.ts mcp-ephemeral — 2 pass with the fix; both fail on the unfixed build with ephemeralAgent.mcp undefined in the captured request payload (regression-proof).
  • Sanity-ran the selector-adjacent mock e2e specs (chat, mcp, model-switching, soft-default, model-spec-branding) — 15 tests pass with the e2e config additions.
  • ESLint and sort-imports:check clean on all changed files.

Test Configuration:

  • Node v24, mock e2e harness (playwright.config.mock.ts) with E2E_BASE_URL=http://localhost:3334 MOCK_LLM_PORT=8899; bug requires modelSpecs configured, which the e2e config already has.

Checklist

  • My code adheres to this project's style guidelines
  • I have performed a self-review of my own code
  • I have commented in any complex areas of my code
  • My changes do not introduce new warnings
  • I have written tests demonstrating that my changes are effective or that my feature works
  • Local unit tests pass with my changes

The no-spec branch of `useApplyModelSpecEffects` (added in #11796) reset
`ephemeralAgentByConvoId` to null on every `newConversation` call when
model specs are configured. On in-place model/endpoint switches (modular
chat, same conversation or new-chat draft), BadgeRowContext never refills
from localStorage — its init effect only re-runs when the storage suffix
or spec changes — so the MCP selection (and tool toggles) were silently
dropped from subsequent request payloads while the MCP badge kept
displaying them.

Reset now only happens on context transitions (leaving a spec, or moving
to a different conversation key), where a BadgeRowContext refill is
guaranteed; in-place non-spec switches preserve the ephemeral agent.

- Gate the no-spec reset on `prevSpecName` / `prevConvoId`, passed from
  `newConversation` via a snapshot read of the pre-switch conversation
- Add jest coverage for all five branches of the no-spec path
- Add e2e spec asserting `ephemeralAgent.mcp` stays in the chat payload
  after a new-chat model switch and after regenerate on a switched
  conversation (verified failing before the fix, passing after)
- Add non-spec "Mock Provider D" endpoint to the e2e config so tests can
  switch between two real ephemeral endpoints; widen `MockEndpoint` type
@danny-avila

Copy link
Copy Markdown
Collaborator Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. 🎉

ℹ️ 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".

@danny-avila
danny-avila merged commit b39ec16 into dev Jun 11, 2026
18 checks passed
@danny-avila
danny-avila deleted the fix/ephemeral-mcp-model-switch branch June 11, 2026 22:13
fuuuzzy pushed a commit to fuuuzzy/LibreChat that referenced this pull request Jun 18, 2026
…Chat-AI#13697)

The no-spec branch of `useApplyModelSpecEffects` (added in LibreChat-AI#11796) reset
`ephemeralAgentByConvoId` to null on every `newConversation` call when
model specs are configured. On in-place model/endpoint switches (modular
chat, same conversation or new-chat draft), BadgeRowContext never refills
from localStorage — its init effect only re-runs when the storage suffix
or spec changes — so the MCP selection (and tool toggles) were silently
dropped from subsequent request payloads while the MCP badge kept
displaying them.

Reset now only happens on context transitions (leaving a spec, or moving
to a different conversation key), where a BadgeRowContext refill is
guaranteed; in-place non-spec switches preserve the ephemeral agent.

- Gate the no-spec reset on `prevSpecName` / `prevConvoId`, passed from
  `newConversation` via a snapshot read of the pre-switch conversation
- Add jest coverage for all five branches of the no-spec path
- Add e2e spec asserting `ephemeralAgent.mcp` stays in the chat payload
  after a new-chat model switch and after regenerate on a switched
  conversation (verified failing before the fix, passing after)
- Add non-spec "Mock Provider D" endpoint to the e2e config so tests can
  switch between two real ephemeral endpoints; widen `MockEndpoint` type
ThomasVuNguyen pushed a commit to ThomasVuNguyen/LibreChat that referenced this pull request Jul 15, 2026
…Chat-AI#13697)

The no-spec branch of `useApplyModelSpecEffects` (added in LibreChat-AI#11796) reset
`ephemeralAgentByConvoId` to null on every `newConversation` call when
model specs are configured. On in-place model/endpoint switches (modular
chat, same conversation or new-chat draft), BadgeRowContext never refills
from localStorage — its init effect only re-runs when the storage suffix
or spec changes — so the MCP selection (and tool toggles) were silently
dropped from subsequent request payloads while the MCP badge kept
displaying them.

Reset now only happens on context transitions (leaving a spec, or moving
to a different conversation key), where a BadgeRowContext refill is
guaranteed; in-place non-spec switches preserve the ephemeral agent.

- Gate the no-spec reset on `prevSpecName` / `prevConvoId`, passed from
  `newConversation` via a snapshot read of the pre-switch conversation
- Add jest coverage for all five branches of the no-spec path
- Add e2e spec asserting `ephemeralAgent.mcp` stays in the chat payload
  after a new-chat model switch and after regenerate on a switched
  conversation (verified failing before the fix, passing after)
- Add non-spec "Mock Provider D" endpoint to the e2e config so tests can
  switch between two real ephemeral endpoints; widen `MockEndpoint` type
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