Skip to content

🛠️ feat: MCP Servers Editor (Wave 2) - #167

Merged
danny-avila merged 43 commits into
mainfrom
feat/config-w2-4-mcp
Oct 23, 2026
Merged

danny-avila merged 43 commits into
mainfrom
feat/config-w2-4-mcp

Conversation

@danny-avila

@danny-avila danny-avila commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

Stack 4/7 of the Wave 2 Configuration-page editors. Based on the previous PR in the stack (feat/config-w2-3-specs). Merge the stack bottom-up, or all at once with the GitHub stack merge.

Summary

MCP servers: purpose-built editor (WS12, MCP-1…MCP-14)

This replaces the generic schema form on the MCP tab with an editor built for MCP servers. The new code is in components/configuration/sections/mcp/.

Server cards

  • The header shows the server's title and key, its transport, where it comes from (librechat.yaml, Added in admin panel, Inherited, or Added in a profile), how many overrides it has, and whether it has unsaved edits.
  • An actions menu offers Rename, Duplicate and Delete. In a profile it also offers Override fields, Hide, and Remove overrides. Disabled items explain why.
  • Delete stays pending with Undo until Save. The pending state comes from the edits, so it survives Discard → Undo.

Fields, grouped by purpose

  • Groups: Connection, Authentication, Display, Behavior, User variables and Advanced.
  • Fields the transport doesn't read are hidden (MCP-3).
  • Each field shows where its value comes from, with Revert to librechat.yaml, or Edited / Pending revert with Undo (MCP-5).
  • Header and user-variable keys set in librechat.yaml cannot be removed.

Transport (MCP-1, MCP-2)

  • stdio is listed but disabled.
  • A stdio card is read-only as a whole. LibreChat's override merge drops every override of a process-backed server, so values saved for one never took effect. The card says so and offers to remove any values stored anyway.
  • Switching transport removes the fields the new transport doesn't use. On a saved server it asks for confirmation inline first.

Authentication (MCP-4)

  • One method at a time: None, API key, OAuth or On-behalf-of.
  • An API key starts as the admin's Bearer key. OAuth basics come first; the advanced options are collapsed.
  • Secrets use password inputs and are redacted in the review.

Other fields (MCP-6 – MCP-8)

  • Optional switches show their real default (Default (On), Auto-detect).
  • Server instructions: Off, Server-provided or Custom text.
  • Timeouts show units and defaults. Descriptions use textareas, URLs are wide, the icon has a preview, and the title has a pattern hint.

Create flow (MCP-9)

  • Name with validation, then transport cards (Streamable HTTP by default and marked recommended), then the same editor.
  • Create stays disabled until the server is valid; the footer names what is missing.

Profiles (MCP-10)

  • Inherited servers are listed read-only until you choose "Override fields", and overrides are written per field.
  • Hide writes a tombstone. It is disabled for librechat.yaml servers, which the backend's registry would show anyway.
  • Hidden servers are listed with Unhide.
  • Creating a server with a name Base already defines becomes "Override ".

Filters and settings (MCP-11, MCP-13)

  • "Set values only" and Expand all now work inside server cards.
  • MCP settings explains the SSRF allowlists with host:port examples, and catalog recovery reads as advanced tuning.

Outside sections/mcp

  • Review: a server renamed and edited in the same save reads as one rename plus its field changes.
  • Errors: a list item's schema error now shows on the list row.
  • onHideField, onUnhideField and onRenameEntry are passed to section renderers. The edit state records each server renamed in the panel, so the review pairs a removal and an addition as a rename only when that rename was made (or the two are identical), never because they share a URL or command.
  • Field metadata: new area file meta/mcp.ts.

Tests

  • Unit tests for transport gating and pruning, auth-method switching, names, and the server list overlay.
  • RTL tests on the real edit hook and value layers.
  • Ported cross-field validator tests.
  • Live Playwright journeys in e2e/mcp.spec.ts, gated by E2E_LIVE=1.

Change Type

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

Testing

  • At this stack level: npx tsc --noEmit (clean), npx vitest run (1986 tests pass) and npx eslint src/ e2e/ (clean).
  • Built in a dedicated worktree and verified live against a real LibreChat backend and MongoDB with a production-like librechat.yaml: acceptance criteria at 1440 and 390 px, light and dark, with screenshots.
  • The full stack was integrated and passed three rounds of independent live verification (functional with DB checks, UX/visual/axe, code review) plus the schema-coverage suite. At the top of the stack: 2400 tests, bun run test:lint-rules and vite build pass.

Test Configuration:

  • Bun 1.4.0; LibreChat dev (data-provider 0.8.527) with MongoDB 8.2

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

Purpose-built AI providers editor (WS9, AP-1…AP-10).
## MCP servers: purpose-built editor (WS12, MCP-1…MCP-14)
Custom endpoints editor (Wave 2, WS10).
Model specs editor (WS11, Wave 2)
@danny-avila
danny-avila added this pull request to stack #171 October 20, 2026 07:38
@danny-avila

Copy link
Copy Markdown
Contributor Author

@codex review

Pass 1. Head: 3fb0ac0

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 22, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-22T16:14:31.552630Z bf90e66 Manual request
ℹ️ 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

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

ℹ️ 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/mcp/auth.ts Outdated
Comment thread src/components/configuration/sections/mcp/Servers.tsx
Comment thread src/components/configuration/review/diff.ts
Comment thread src/components/configuration/sections/mcp/Servers.tsx Outdated
Comment thread src/components/configuration/sections/mcp/Transport.tsx Outdated
The configured-only filter now treats a path holding an unsaved value as set: provider groups, generic rows, sections and table-of-contents entries stay shown, so a pending edit of an unset setting never disappears behind the filter. isPathSet moves to counts.ts as the single rule.
- An inherited requiresOAuth: false no longer locks the API key and On-behalf-of methods.
- Deleting a server renamed in this session lists the saved server as pending deletion, with Undo bringing the rename back.
- The review pairs a removed and an added server as a rename only when the panel recorded the rename (or the values are identical), not because they share a URL or command.
- Duplicate skips masked secret previews, notes which stored secrets must be entered again, and Save requires a key for an admin-provided API key.
- A transport switch waiting for confirmation is planned from the current entry and dropped when the edit session ends.
@danny-avila

Copy link
Copy Markdown
Contributor Author

@codex review

Pass 2. Head: 4ebfc09

@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: 4ebfc09eb4

ℹ️ 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/mcp/Servers.tsx Outdated
Comment thread src/components/configuration/sections/mcp/Servers.tsx
Comment thread src/components/configuration/sections/mcp/Servers.tsx
Comment thread src/components/configuration/sections/mcp/names.ts Outdated
Comment thread src/components/configuration/sections/mcp/Field.tsx
Collapsed provider cards and groups summarized a pending API key as plain text. Secret fields (as the field renderer detects them) now show the masked placeholder; environment references stay visible.
Undo inserted a removed endpoint at its loaded position in the shortened pending list, so undoing B after deleting A and B from [A, B, C] gave [C, B]. It now goes back before the first surviving endpoint that was loaded after it.
…ues Only

In a profile, the first pending edit of an inherited endpoint is not yet a stored override, so the set-values-only filter hid the row as soon as its drawer closed. Edited rows now stay listed.
Override a built-in parameter appended only { key, default }. LibreChat expands librechat.yaml definitions at startup but serves an override's as stored, and its client replaces the built-in definition with it whole, so the parameter lost its type, component and label. The full built-in definition is now stored (optionType custom, as LibreChat expands it), and a definition that differs from its built-in only in its default still opens as a default override.
The headers editor remembered every focused text input, header names included, so picking a placeholder after focusing a name wrote it into the name. Key/value inputs now carry data-kv-part, only value inputs are tracked, and with no value focused the placeholder is copied instead.
Entry ops placed removes after upserts, so a new spec's index counted entries that were about to be removed: deleting A and appending X to [A, B, C] stored [B, X, C]. Removes now come first, so every index counts only the entries that stay.
With Set values only on, a new spec ({}) hid Basics and the empty Endpoint and Model rows, so it could not be completed. A required field that is still empty (it has a required error) now stays visible.
Spec save issues dropped every name problem, assuming the pipeline reports them. The pipeline keys entries by the name as written, so a whitespace-only name, or names differing only by surrounding spaces, showed an inline error but left Save enabled. Only the name problems the pipeline reports itself are dropped now.
Spec search indexed every prompt field, so an OpenAI spec offered a System prompt result that opened no row. The editor and the search index now share specPromptKeys: the endpoint's prompt field, the greeting, the assistants' instructions on an assistants endpoint, and any other prompt field the preset already sets (which the editor now also shows, so a stale value stays removable).
The rename warning compared with the name captured when the Name field first mounted, which a save or scope change does not reset: after renaming A to B and saving, renaming B again showed no warning. The loaded name now comes from the pending collection edit's origin (or the current name while the list has no pending edit).
Model specs are replaced whole, but the keyed field reader fell back to the lower layer's spec of the same name, so an owned spec without a description was shown as using librechat.yaml's (or, in a profile, Base's). A layer that owns its list now stops lower values from counting as effective, configured or in the provenance stack; the same-named lower entry stays only as the revert target.
Switching an agents spec to another endpoint kept agent_id in the preset, while the editor hid the Agent field and left agent_id out of the parameter rows, so the stale value could be neither seen nor removed. Leaving the agents endpoint now clears it, and an agent ID a non-agents spec still stores (e.g. from librechat.yaml) is shown as a removable row.
…ords on Delete

Renaming a saved MCP server writes it under a new name without its stored secrets (only their masked previews are loaded), and nothing blocked saving a renamed OAuth server without its client secret. A renamed server now lists the stored secrets to enter again, and Save is blocked until they are, using the page's rename records.

Deleting a renamed server only cleared the card's local rename note, so the page still recorded the rename and the review paired an unrelated new server of that name with it. Deleting it now drops the rename record (onRenameEntry(from, null)), and Undo records it again.
…es Only

In a profile with Set values only, choosing Override for an inherited server from Create only unlocked it, but the filter still dropped the card (no profile value yet), and its fields would have been filtered too. An unlocked server now stays listed with all of its fields, so the first override can be entered.
copyName tested candidates case-sensitively while server names must be unique in any letter case, so duplicating Docs next to docs-copy created Docs-copy. Candidates are now compared lowercased.
The admin API returns a stored apiKey.key or oauth.client_secret only as its preview companion, but MCP fields passed no preview to the control, so a configured credential looked like an empty password input. The row now shows the masked preview with Replace, and Cancel discards the replacement.
@danny-avila

Copy link
Copy Markdown
Contributor Author

@codex review

Pass 3. Head: 666288f

@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: 666288f141

ℹ️ 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/review/revert.ts Outdated
Comment thread src/components/configuration/sections/mcp/validation.ts
Comment thread src/components/configuration/sections/mcp/Servers.tsx
@danny-avila

Copy link
Copy Markdown
Contributor Author

@codex review

Pass 4 of 4 (final review). Head: bf90e66

This is the final review: please flag only blocking issues.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Delightful!

Reviewed commit: bf90e661fa

ℹ️ 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 removed this pull request from stack #171 October 23, 2026 04:44
@danny-avila
danny-avila changed the base branch from feat/config-w2-3-specs to main October 23, 2026 04:49
@danny-avila
danny-avila merged commit acc25aa into main Oct 23, 2026
3 checks passed
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