Skip to content

🐛 fix: Keep KV Record Edits Object-Typed so Headers Never Save as Arrays - #111

Open
dustinhealy wants to merge 2 commits into
mainfrom
fix/issue-56-mcp-header-kv-serialization
Open

🐛 fix: Keep KV Record Edits Object-Typed so Headers Never Save as Arrays#111
dustinhealy wants to merge 2 commits into
mainfrom
fix/issue-56-mcp-header-kv-serialization

Conversation

@dustinhealy

@dustinhealy dustinhealy commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Summary

MCP custom headers (and other KeyValueField-backed record fields like env and azure addParams) could reach the backend as arrays instead of records. The version current when #56 was filed serialized non-indexed save paths with the shallow serializeKVPairs while the MCP renderer wrote whole objects, so header pairs nested inside those values persisted verbatim as [{key, value, valueType}], which LibreChat's schema rejects. #92 fixed the primary path by deep-serializing every entry in buildSavePayload, but two leaks remain on main.

First, emptying a KeyValueField (removing the last row) stores a bare [], which shape-based serialization cannot recognize as KV pairs, so it is submitted as an array where the schema expects an object. On the base-config route this fails validation with "Expected object, received array" and blocks the whole save; on the profile-value route, which performs no field validation, the invalid array persists.

Second, FieldProfilePopover serialized modal values with the shallow serializeKVPairs and getDefaultValue seeded record fields with [], so an untouched "add profile value" save on a record field persisted a raw array through the unvalidated route.

Record-control edits now collapse an emptied pair list to {} via a shared kvPairsEditValue helper (both FieldRenderer record branches and ProfileValueModal), getDefaultValue returns {} for record fields, and FieldProfilePopover serializes modal values with deepSerializeKVPairs so no route can leak a pairs array. In-progress rows still round-trip as raw pairs in edit state so blank keys survive re-renders until save-time serialization.

Fixes #56

Change Type

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

Testing

Regression tests cover the #56 repro end to end (DOM edit of an Authorization header through applyConfigEdit and buildSavePayload producing {Authorization: "Bearer {{API_KEY}}"}), the emptied-list cases in the MCP renderer, FieldRenderer, and ProfileValueModal (all fail before this change), and pairs nested inside indexed array entries matching the azure "Additional parameters" report on #44. Local gates: full vitest suite passes (810 tests), eslint --max-warnings 0 clean, tsc --noEmit 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

Emptying a KeyValueField (removing the last header, env, or addParams row) stored a bare [] in edit state. serializeKVPairs cannot recognize an empty array as KV pairs, so the [] reached the backend as an array where the schema expects a record, failing base-config validation with "Expected object, received array" and persisting invalid values on the unvalidated profile-value route.

Record-control edits now collapse an emptied pair list to {} via a shared kvPairsEditValue helper (FieldRenderer record branches and ProfileValueModal), getDefaultValue returns {} for record fields so an untouched profile-value save stays object-typed, and FieldProfilePopover serializes modal values with deepSerializeKVPairs so no nested pairs shape can leak through the one save route that bypasses buildSavePayload.

Adds regression tests proving MCP header edits serialize to a plain record in the save payload (issue #56 repro) and that emptied KV lists emit {} rather than [].
@dustinhealy

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. What shall we delve into next?

Reviewed commit: b522a1184c

ℹ️ 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
Comment thread src/components/configuration/FieldProfilePopover.tsx Outdated
Serializing profile modal values shape-based would collapse any array whose elements happen to carry key and value properties into a record, dropping sibling fields. Only the record control edits via KeyValueField pairs, so serialize only that control type through a shared serializeModalValue helper and pass every other control's value through untouched.
@dustinhealy

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. More of your lovely PRs please.

Reviewed commit: c008220d19

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

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] Unable to save custom MCP headers from admin panel

1 participant