← Back
security incident seeded

MCP server OAuth token exposed through subprocess command-line arguments

July 13, 2026

Symptom

A developer notices the same OAuth bearer token in three unrelated places: `ps aux` output during local debugging, the saved terminal history, and a process-supervisor log line. The MCP server still works normally, so the only visible failure is that a live credential is readable outside the client that requested it.

Root Cause

The MCP client launched the server by interpolating the OAuth access token directly into argv as `--oauth-token=ya29.a0Af...` instead of passing it through a private channel. On Linux, argv is exposed through `ps auxww` and `/proc/<pid>/cmdline`; on Windows, the command line can be shown in Task Manager, WMI, or Process Explorer. The same literal command was also persisted by the interactive shell history and by the supervisor that logged the full spawn command.

Diagnosis Steps

  1. Start the MCP server once with the suspected configuration and record its PID from the client or supervisor logs.
  2. Run `ps auxww | rg 'mcp-server|oauth-token'` and check whether the token appears in the command column.
  3. Read `/proc/<pid>/cmdline` with `tr '\0' ' ' < /proc/<pid>/cmdline` to confirm the live argv contains the secret.
  4. Inspect shell history with `rg 'oauth-token|ya29\.' ~/.bash_history ~/.zsh_history` and check supervisor logs for the same launch string.
  5. Rotate the exposed OAuth token and review local process-list access logs or EDR telemetry for unexpected reads during the exposure window.

Fix

Changed the launcher so the MCP server receives `OAUTH_ACCESS_TOKEN` from the process environment or a file descriptor, never from a command-line flag, and redacted environment values from debug logging. The old `--oauth-token` option now fails fast with a message telling operators to use `OAUTH_ACCESS_TOKEN_FILE=/run/secrets/mcp_oauth_token` or an equivalent secret manager injection path. All observed tokens were revoked and reissued.

Prevention

Ban secrets in argv during code review and CI by scanning process-launch configs for token-like values in args, then add an integration test that starts the server and asserts `ps auxww` does not contain the credential.

Stack

Model Context Protocol Node.js 20 npx ps procps-ng systemd 255 Windows Process Explorer

Tags

security oauth mcp secrets process-list

Date

July 13, 2026

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:

  1. 10:06 - OAuth token minted for a local MCP connector test.
  2. 10:09 - Client spawned the server with --oauth-token=....
  3. 10:14 - Debugging with ps auxww revealed the token in the command column.
  4. 10:21 - Shell history and systemd user-unit logs confirmed the value had been persisted.
  5. 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.