Skip to content

🔄 fix: Per-Entry Reset to YAML for MCP Servers & Custom Endpoints - #113

Open
dustinhealy wants to merge 5 commits into
mainfrom
fix/issue-73-108-clear-yaml-overrides
Open

🔄 fix: Per-Entry Reset to YAML for MCP Servers & Custom Endpoints#113
dustinhealy wants to merge 5 commits into
mainfrom
fix/issue-73-108-clear-yaml-overrides

Conversation

@dustinhealy

@dustinhealy dustinhealy commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Summary

An MCP server or custom endpoint defined in librechat.yaml and then edited in the admin panel gets DB overrides that can never be cleared. For MCP servers the trashcan is hidden for YAML-defined entries, and even editing the YAML has no effect because the stored override is deep-merged over it on every load. For custom endpoints the situation is worse: LibreChat merges the endpoints.custom override array into the YAML array by name and preserves unmatched YAML items, so deleting a YAML-defined endpoint silently fails (it reappears on reload), renaming one duplicates it, and any save copies the entire merged array into the override document, freezing every YAML endpoint against future YAML changes.

YAML-defined entries with stored overrides now show a per-entry "reset to YAML" action on the entry card (base config mode only). After confirmation it clears just that entry's override layer: MCP servers unset the mcpServers.<key> subtree in the base override document; custom endpoints rewrite the stored endpoints.custom override array without the name-matched item, or unset the array path when it was the only override item. The endpoint rewrite is built from a freshly fetched override document rather than the client cache, so a concurrent admin's change to the array is never discarded by a stale read-modify-write, and an item already removed by someone else resolves as a no-op. Stored endpoint secrets survive the rewrite via the backend's omit-to-keep name-identity preservation (rewritten arrays carry redacted siblings with no secret keys, the exact shape preserveArraySecrets restores), and masked previews are stripped client-side before the write. The action is disabled while unsaved edits are pending, matching the global reset button, and a successful reset bumps the edit session so open-but-untouched secret Replace inputs remount instead of surviving the reset (the MCP renderer now threads the session id down to its fields for this). Resets target only the base override document (the __base__ role config); scope-profile overrides for roles, groups, and users are unaffected.

Custom endpoints defined in YAML also now hide the trashcan (deletion is impossible at the merge layer and previously reappeared on reload) and lock the name field, since name is the merge identity and renaming orphans the override while spawning a duplicate entry. Admin-created endpoints keep delete and rename, and scope mode is unchanged.

Review hardening rounds added three robustness cases: override documents that store endpoints.custom in the legacy numeric-key object form (written by pre-#92 indexed-path saves) are normalized so those entries still get the reset action and a correct rewrite; when the YAML-provenance fetch fails while the main config load succeeds, custom endpoint identity actions fail closed (trash hidden, name locked, reset hidden) instead of presenting YAML endpoints as admin-created; and MCP keys the field-path validator rejects (__proto__, constructor, prototype) get the same read-only treatment as dotted legacy keys, since no field-path write or unset for them can succeed.

Fixes #73

Fixes #108

Change Type

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)

Testing

Unit coverage spans the reset-plan builder (buildEntryOverridesResetPlan, including legacy numeric-key object storage and the sibling-secret omit-shape contract), the fresh-document reset flow (executeEntryOverridesReset, including the concurrent-edit race and already-removed no-op), the MCP per-entry reset affordance (shown only for overridden YAML servers in base mode, disabled while dirty, hidden for validator-rejected keys), edit-session remounting of MCP secret fields, provenance fail-closed behavior, and the first CustomEndpointsRenderer tests covering trash hiding, name locking, and the reset target payload. The new behavior tests fail on main and pass on this branch; full suite is 833 passing with eslint and tsc clean.

Checklist

  • My code adheres to this project's style guidelines
  • I have performed a self-review of my own 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

YAML-defined MCP servers and custom endpoints edited in the admin panel accumulate DB overrides that could never be cleared. The delete affordance was hidden (MCP) or silently ineffective (custom endpoints, where LibreChat merges the override array into the YAML array by name and re-adds unmatched YAML items), leaving entries permanently shadowed even after librechat.yaml changed.

Each YAML-defined entry with stored overrides now shows a reset action that, after confirmation, clears its override layer: MCP servers unset the mcpServers.<key> subtree in the base override document; custom endpoints rewrite the endpoints.custom override array without the name-matched item, or unset the array path when it was the only override. The rewrite is built from a freshly fetched override document rather than the client cache, so a concurrent admin's change to the array is never discarded by a stale read-modify-write; an item already removed resolves as a no-op. The action is base-mode only and locked while unsaved edits are pending. Scope-profile overrides are untouched.

Custom endpoints defined in YAML also hide the trash button (deletion is impossible at the merge layer and previously reappeared on reload) and lock the name field, since name is the merge identity and renaming orphans the override while duplicating the entry.

Fixes #73
Fixes #108
@dustinhealy

Copy link
Copy Markdown
Contributor Author

@codex review

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

Copy link
Copy Markdown

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: 8a8a2e1d94

ℹ️ 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 thread src/components/configuration/utils.ts
Comment thread src/components/configuration/ConfigPage.tsx Outdated
Comment thread src/components/configuration/ConfigPage.tsx
Comment thread src/components/configuration/sections/McpServersRenderer.tsx
…e After Entry Reset

Override documents written by panel versions before #92 PATCHed indexed field paths (endpoints.custom.2) directly, which Mongo's dot-path $set materializes as a numeric-key object instead of an array. The reset affordance and the rewrite plan only recognized the array shape, so endpoints overridden through such documents showed no reset action and could not be cleared. toOverrideArraySource now normalizes both shapes into a dense array, and the entry-key derivation moved into collectEntryOverrideKeys so it shares the normalization and is unit-tested.

A successful entry reset now also bumps the edit session id, remounting session-keyed field state such as a SecretField opened for Replace but left untouched, which query invalidation alone does not reach.
@dustinhealy

Copy link
Copy Markdown
Contributor Author

@codex review

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

Copy link
Copy Markdown

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: 1fbd35dc79

ℹ️ 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 thread src/server/config.ts
…enance Is Unavailable

When the auxiliary baseOnly request failed while the main config request succeeded, yamlCustomEndpointKeys came back undefined and the client coerced it to an empty set, presenting every YAML-defined custom endpoint as admin-created: rename and delete enabled, reset hidden, and a staged delete would both fail at the merge layer and freeze the whole array into the override document.

extractCustomEndpointNames now always returns an array for a successfully fetched baseOnly config, so a missing or malformed endpoints.custom means known-empty provenance and undefined yamlCustomEndpointKeys occurs only when the provenance fetch itself failed. CustomEndpointsRenderer treats that undefined as provenance-unavailable and fails closed, hiding the trash, locking the name field, and hiding the reset action for every entry until provenance is known, while the config editor keeps rendering.
@dustinhealy

Copy link
Copy Markdown
Contributor Author

@codex review

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

Copy link
Copy Markdown

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: 2c5bb90186

ℹ️ 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 thread src/components/configuration/utils.ts
Comment thread src/components/configuration/sections/McpServersRenderer.tsx
The MCP renderer never accepted or forwarded editSessionId, so SecretFields inside MCP entry cards were keyed with a constant session and kept local replacement state across every edit-session bump: discard, save, base reset, and the per-entry overrides reset. An opened Replace input therefore survived a successful reset instead of returning to the masked display.

editSessionId now flows from the renderer props through McpEntryRow and McpEntryFields into renderInlineField, matching how the endpoints renderer already forwards it, so session-keyed secret fields remount whenever ConfigPage bumps the session.

Also pins the client contract the endpoint reset relies on: rewritten override arrays carry sibling items in the omit shape (no apiKey or apiKeyPreview keys, passthrough env references verbatim) that the backend's name-identity secret preservation expects.
@dustinhealy

Copy link
Copy Markdown
Contributor Author

@codex review

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

Copy link
Copy Markdown

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

ℹ️ 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 thread src/components/configuration/sections/McpServersRenderer.tsx
A YAML MCP server keyed __proto__, constructor, or prototype showed the per-entry reset action, but safeFieldPath rejects those segments so the confirm always failed server-side. Such keys now get the same read-only treatment as dotted legacy keys, since every field-path write or unset for them fails the same validation.
@dustinhealy

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Can't wait for the next one!

Reviewed commit: 426525327b

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

@dustinhealy
dustinhealy marked this pull request as ready for review August 10, 2026 03:33
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.

[BUG] Custom Endpoints added via config cannot be deleted or edited via the Admin Panel Clear Overrides not available on important features

1 participant