fix(mcp): inject keyring credentials into MCP server child process#17
Conversation
The deployhq-mcp-server reads credentials from DEPLOYHQ_ACCOUNT, DEPLOYHQ_EMAIL, and DEPLOYHQ_API_KEY env vars and calls process.exit(1) if any is missing. But `dhq mcp` was just spawning the binary with c.Env = os.Environ() — the user's keyring-stored credentials never reached the child process. Telemetry showed 3 of 4 mcp invocations (75%) failing with "MCP server exited: exit status 1", all from users with no agent env wrapper (no codex/claude-code/warp shell) on darwin. These are people who ran `dhq auth login` (creds → keyring), then `dhq mcp`, and got a silent crash because the child couldn't see the keyring. Changes: - Extract credential resolution into a Context.Credentials helper that returns the resolved (account, email, apiKey) triplet using the same precedence as Client(): flags → env → file → keyring. - Refactor Client() to use it (no behavior change for that path). - In `dhq mcp`, call Credentials() before spawning the server and merge the three DEPLOYHQ_* vars into the child env if they aren't already set by the user. - mergeEnv preserves any explicit env override the user set (e.g. pointing the server at a different account for one session) and skips empty values. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
🚧 Files skipped from review as they are similar to previous changes (2)
WalkthroughThis PR extracts credential resolution into a reusable ChangesCredential Resolution and MCP Environment Injection
🎯 3 (Moderate) | ⏱️ ~20 minutes 🚥 Pre-merge checks | ✅ 4✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 2c32144df0
ℹ️ 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".
| if _, ok := present[k]; ok { | ||
| continue |
There was a problem hiding this comment.
Treat empty credential env vars as missing
When a user has an empty exported credential like DEPLOYHQ_API_KEY= and valid keyring credentials, Credentials() resolves the real key, but mergeEnv marks the empty variable as present and skips injecting the resolved value. The MCP server still receives an empty required env var and exits at startup, so this leaves a common “env var exists but is blank” variant of the keyring-login failure unfixed.
Useful? React with 👍 / 👎.
Codex review on PR #17 caught a residual failure mode: when a user has exported a credential as empty (e.g. DEPLOYHQ_API_KEY= with no value) alongside valid keyring credentials, the previous mergeEnv saw the key as present and skipped injecting the resolved value, so the MCP server still received a blank required env var and exited at startup. mergeEnv now drops empty exported entries for keys we're about to override, and the override is appended in their place. Non-empty user exports are still preserved (power-user override path), and unrelated empty exports (e.g. OTHER_THING=) are left alone. Two new test cases cover the empty-export drop and the unrelated-key preservation. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Summary
Telemetry showed 3 of 4
dhq mcpinvocations failing withMCP server exited: exit status 1(all darwin, all non-agent shells on 0.16.3 / 0.14.1). Root cause: thedeployhq-mcp-servernpm package reads credentials fromDEPLOYHQ_ACCOUNT,DEPLOYHQ_EMAIL,DEPLOYHQ_API_KEYenv vars and callsprocess.exit(1)if any is missing — butdhq mcpwas spawning the binary withc.Env = os.Environ(), so users who logged in viadhq auth login(creds → keyring) silently never had those vars and the server crashed at startup.Changes
Context.Credentials()returning the resolved(account, email, apiKey)triplet using the same precedence asClient(): flags → env → file → keyring.Client()refactored to use it — no behavior change for the SDK client path.dhq mcpnow callsCredentials()before spawning the server and merges the threeDEPLOYHQ_*env vars into the child's environment if they aren't already set.mergeEnvpreserves any explicit env override the user set (so power users can still point the server at a different account for one session) and skips empty values.Test plan
go build ./...,go vet ./...,go test ./...,golangci-lint runTestMergeEnvcovers: adds missing keys, preserves user-set keys, skips empty override values.dhq auth login, thendhq mcpin a fresh shell with noDEPLOYHQ_*exports — server stays running.DEPLOYHQ_ACCOUNT=other dhq mcp— user override wins.🤖 Generated with Claude Code
Summary by CodeRabbit