Skip to content

fix: preserve env sub-tables instead of flattening into phantom servers#3

Merged
justrach merged 2 commits into
mainfrom
fix/preserve-env-subtables
Jun 1, 2026
Merged

fix: preserve env sub-tables instead of flattening into phantom servers#3
justrach merged 2 commits into
mainfrom
fix/preserve-env-subtables

Conversation

@justrach

@justrach justrach commented Jun 1, 2026

Copy link
Copy Markdown
Owner

The bug

mcpsync's Server model had no env, so a sync would:

  1. Create a phantom transport-less server from Codex's nested [mcp_servers.NAME.env] sub-table — readServersToml read it as a server literally named NAME.env, then wrote it back as [mcp_servers."NAME.env"]. Codex rejects that with invalid configuration: invalid transport in mcp_servers.NAME.env.
  2. Silently drop env vars (API keys!) off any server that had them — the model never captured env on read, so it was gone on write.

This was hit in the wild: a codedb sync corrupted ~/.codex/config.toml (node_repl.env) and created malformed gitagent.env entries in Windsurf/Devin/Gemini/OpenCode.

The fix — real env support (mirrors headers)

  • config.Server gains an env string-map, wired through dupe/free/eql and the source-of-truth read (parseRegistry) + write (writeMcpServers).
  • JSON targets read "env" and write it back (reuses writeJsonHeadersField).
  • TOML (Codex)readServersToml now routes [mcp_servers.NAME.env] into the pending server's env (and also reads inline env = { ... }), and skips any dotted .env/.headers section so a phantom server can never be emitted. This also cleans up phantoms a previous mcpsync wrote. env is written back as an inline table.

Tested

  • Unit tests (TOML sub-table, inline TOML) added to targets.zig; zig build test green.
  • Verified end-to-end on real configs: env preserved, no phantom, mcpsync diff clean across all 7 tools.

Behavior-preserving for servers without env; purely additive otherwise.

mcpsync had no model for a server's `env`, so syncing either:
  • created a phantom transport-less server from Codex's nested
    `[mcp_servers.NAME.env]` sub-table (read as a server literally named
    "NAME.env") — which Codex then rejects with "invalid transport", and
  • silently dropped env vars (API keys!) off any server that had them,
    since the Server model never captured env on read or wrote it back.

Add real `env` support, mirroring `headers`:
  • config.Server gains an `env` string-map; wired through dupe/free/eql
    and the source-of-truth read (parseRegistry) + write (writeMcpServers).
  • JSON targets: read `"env"` and write it back (reuses writeJsonHeadersField).
  • TOML (Codex): readServersToml now routes `[mcp_servers.NAME.env]` into the
    pending server's env (and also reads inline `env = { ... }`); it skips any
    dotted `.env`/`.headers` section so no phantom server is ever emitted —
    which also cleans up phantoms a previous mcpsync wrote. Writes env back as
    an inline table.

Round-trip tested (TOML sub-table, inline TOML, JSON) and verified on real
configs: env preserved, no phantom, `diff` clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@justrach justrach merged commit 3fe5121 into main Jun 1, 2026
ozgurulukir pushed a commit to ozgurulukir/codedb that referenced this pull request Jun 9, 2026
… from installer

The mcpsync-based registration added in the Windsurf/Devin PR could corrupt a
user's configs: mcpsync (≤0.0.4) flattened a server's nested env into a phantom
transport-less server and dropped env vars. (Fixed upstream in
justrach/mcpsync#3, but the installer auto-installs whatever mcpsync is live.)

Windsurf and Devin both use a standard mcpServers JSON object, so register
codedb the same way the other tools are — a direct, additive python write of
just the codedb entry, guarded by the tool's presence. This:
  • can't drop a server's env/headers (only the codedb key is written),
  • can't create phantom servers,
  • needs no mcpsync (no extra download / network dependency),
  • preserves Devin's non-mcpServers peer keys.

nuke.zig already deregisters both (unchanged). Verified in a sandbox: env-
bearing servers and peer keys preserved, codedb added, ~/.mcpconfig.json never
touched. install.sh is `bash -n` clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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