feat(mcp): resolve secrets into project-scope configs for all tools - #258
Merged
Conversation
Previously, project-scope MCP config files for tools that cannot expand
${VAR} themselves (Cursor, CodeBuddy) skipped any secret-bearing server,
to avoid writing a resolved secret into a committed file. Teams that
accept committing the secret (or gitignore the config) had no way to
distribute such a server at project scope.
Drop the project-scope refusal: the value is now resolved and written
verbatim for every tool, in every scope. Claude project .mcp.json still
passes the placeholder through, and Codex still names the env var, so
those keep secrets off disk as before. Docs gain a warning to gitignore
.cursor/mcp.json / .codebuddy/mcp.json or use user scope if the secret
must stay out of version control.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
4 tasks
5 tasks
jeff-r2026
added a commit
that referenced
this pull request
Jul 29, 2026
…rets off disk (#260) CodeBuddy and Cursor were getting remote (http/sse) MCP servers written with `transportType: "streamable-http"`, a field both tools ignore — so the Authorization header never shipped and the server 401'd. Both key the transport off `type`, exactly like the claude family. Emit `type` for them. While fixing the format, restore placeholder passthrough for these two tools so team secrets no longer land on disk (undoing that part of #258 for them): - CodeBuddy interpolates the bare `${VAR}` in every scope — pass it through. - Cursor interpolates `${env:NAME}` — rewrite `${VAR}` into that syntax. `supportsEnvExpansion` now returns true for cursor/buddy; the renderers carry the per-tool placeholder syntax. The resolve-to-plaintext path remains only as a fallback (user-scope claude, codex placeholders it cannot name as a whole header). Verified end-to-end against the real test repo in a project-scope install: claude keeps `${VAR}`, codebuddy `type:"http"` + `${VAR}`, cursor `type:"http"` + `${env:VAR}`; a POST to the backend with the resolved token returns HTTP 200, confirming the header now ships. Docs (EN + zh-CN) updated. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
When distributing team MCP servers at project scope, only Claude Code received secret-bearing servers. Cursor and CodeBuddy silently skipped any server whose headers/URL referenced a
${VAR}(e.g.Authorization: Bearer ${GPU_ANALYSIS_TOKEN}).Root cause:
reconcileMcpForConfigrefused to resolve a placeholder for tools that cannot expand${VAR}themselves when the target file is project-scoped (committed to the repo), to avoid leaking a plaintext secret into version control. Only Claude's project.mcp.json(expands${VAR}itself) and Codex (bearer_token_env_var) kept the placeholder, so only they got the server.Reproduced against the test repo
https://git.woa.com/teamai/teamai-dev-repo(gpu-analysisserver):claude/gpu-analysis added, butcursor/gpu-analysis skipped,codebuddy/gpu-analysis skipped.Change
Drop the project-scope refusal. The value is now resolved and written verbatim for every tool, in every scope. The only remaining skip reason for secrets is a genuinely unresolved variable.
.mcp.jsonstill passes the placeholder through (Claude expands it).bearer_token_env_var/env_http_headers).0600).Docs (both languages) gain a warning: gitignore
.cursor/mcp.json/.codebuddy/mcp.json, or distribute secret-bearing servers at user scope, if the secret must stay out of version control.Test Plan
npx tsc --noEmit— no errorsnpx vitest run— 1814 passed (140 files). Rewrote themcp-reconciletest that previously asserted the skip, to assert the resolved value is now written for Cursor.npm run build— successteamai-dev-repo, project scope,GPU_ANALYSIS_TOKENset):cursor/gpu-analysis skipped,codebuddy/gpu-analysis skippedcursor/gpu-analysis added,codebuddy/gpu-analysis added.cursor/mcp.json&.codebuddy/mcp.jsoncontainAuthorization: "Bearer <resolved-token>"at mode0600;.mcp.jsonkeepsBearer ${GPU_ANALYSIS_TOKEN}.🤖 Generated with Claude Code