npx @acme/mcp-server --transport stdio --oauth-token=ya29.a0AfH6SM...
The incriminating launch line was ordinary enough to miss in review.
Nothing crashed. The server authenticated, tools listed correctly, and the client could call them. The problem was that the token had left the authentication boundary and become process metadata. Any local account that could list processes could see it:
ps auxww | rg 'mcp-server|oauth-token'
tr '\0' ' ' < /proc/41822/cmdline
That second command was the proof. /proc/41822/cmdline did not show an audit event, a request header, or an application log; it showed the kernel’s copy of argv, null-separated, including the bearer token. On a shared build host, that made the live credential available to unrelated jobs running under different users. On a developer workstation, the same value appeared again in ~/.zsh_history because the reproduction command had been typed by hand.
Timeline:
- 10:06 - OAuth token minted for a local MCP connector test.
- 10:09 - Client spawned the server with
--oauth-token=....
- 10:14 - Debugging with
ps auxww revealed the token in the command column.
- 10:21 - Shell history and
systemd user-unit logs confirmed the value had been persisted.
- 10:31 - Token revoked; launcher switched to
OAUTH_ACCESS_TOKEN_FILE.
The fix deliberately removed the convenient flag instead of documenting it as dangerous. Secrets passed as arguments are already too public by the time application code starts running. The replacement path reads the token from /run/secrets/mcp_oauth_token, so argv contains only npx @acme/mcp-server --transport stdio and ps auxww never receives the bearer value. Debug logging also redacts OAUTH_ACCESS_TOKEN if it prints the server environment.