[DOCS] `CLAUDE_CODE_SHELL_PREFIX` docs omit stdio MCP argument-preservation guidance

Status Fixed / completed
Maintainer reply None cached
Activity 3 comments · opened May 5, 2026 · closed Jun 13, 2026

Documentation Type

Unclear/confusing documentation

Documentation Location

https://code.claude.com/docs/en/env-vars

Section/Topic

CLAUDE_CODE_SHELL_PREFIX in Environment variables, plus the stdio MCP server startup guidance in MCP configuration

Current Documentation

The env vars page currently says:

CLAUDE_CODE_SHELL_PREFIX | Command prefix that wraps shell commands Claude Code spawns: Bash tool calls, hook commands, and stdio MCP server startup commands. Useful for logging or auditing. Example: setting /path/to/logger.sh runs each command as /path/to/logger.sh <command>

The MCP docs currently say:

claude mcp add [options] <name> -- <command> [args...]

and later:

Command arrays must match exactly - both the command and all arguments in the correct order

What's Wrong or Missing?

The docs say that CLAUDE_CODE_SHELL_PREFIX wraps stdio MCP startup commands, but they do not explain the argument-forwarding contract for that wrapper.

That omission matters because stdio MCP servers are configured as exact command/argument arrays, and v2.1.128 fixed a bug where those arguments could be corrupted when the prefix was set and an argument contained spaces or shell metacharacters.

Today the docs do not tell readers:

  • whether the original stdio command and its arguments are preserved as separate argv entries
  • how a logging/auditing wrapper should safely re-exec the wrapped command
  • that rebuilding the command as a shell string can break MCP server startup or exact serverCommand matching

Suggested Improvement

Add a short note to CLAUDE_CODE_SHELL_PREFIX and cross-reference it from the stdio MCP section.

Suggested content:

When CLAUDE_CODE_SHELL_PREFIX wraps a stdio MCP server startup command, the original command and arguments should be forwarded without flattening or re-quoting them. If your wrapper re-executes the command, pass the original argv through exactly.

Example wrapper guidance:

#!/usr/bin/env bash
# log the command here if needed
exec "$@"

Also add a warning not to use eval, $*, or unquoted expansions for wrappers that may handle arguments containing spaces or shell metacharacters.

Impact

Medium - Makes feature difficult to understand

Additional Context

Affected Pages:

| Page | Line(s) | Context |
|------|---------|---------|
| https://code.claude.com/docs/en/env-vars | 137 | CLAUDE_CODE_SHELL_PREFIX says it wraps stdio MCP startup commands but does not explain argv preservation |
| https://code.claude.com/docs/en/mcp | 292-307, 1331-1338 | stdio servers are configured as <command> [args...] and command-based restrictions require exact argument matching |

Total scope: 2 pages affected

Relevant release note: v2.1.128 fixed corrupted stdio MCP arguments when CLAUDE_CODE_SHELL_PREFIX was set and an argument contained spaces or shell metacharacters.

View original on GitHub ↗

3 Comments

coygeek · 4 months ago

checked docs, not resolved yet

coygeek · 4 months ago

Issue #56161 Verification

Title: [DOCS] CLAUDE_CODE_SHELL_PREFIX docs omit stdio MCP argument-preservation guidance
Issue Date: 2026-05-05
Verification Date: 2026-06-12
Status: RESOLVED

---

Issue Summary

This issue reports that the CLAUDE_CODE_SHELL_PREFIX environment variable documentation does not explain the argument-forwarding contract when the prefix wraps stdio MCP server startup commands. The reporter asked for three things to be documented:

  1. Whether the original stdio command and its arguments are preserved as separate argv entries
  2. How a logging/auditing wrapper should safely re-exec the wrapped command
  3. That rebuilding the command as a shell string can break MCP server startup or exact serverCommand matching

---

Verification Results

Claim 1: CLAUDE_CODE_SHELL_PREFIX entry omits the argument-forwarding contract for stdio MCP startup commands

Status: RESOLVED

The current entry for CLAUDE_CODE_SHELL_PREFIX at https://code.claude.com/docs/en/env-vars, line 248, has been substantially expanded to address the reporter's three concerns. The current text reads (key clause):

"The wrapper receives the command line as a single shell-quoted argument in $1, so the wrapper must re-evaluate $1 with a shell, for example exec bash -c "$1". Treating $1 as a bare executable path breaks stdio MCP servers that pass arguments such as npx -y <package>. For Bash tool calls, $1 contains the full shell invocation Claude Code assembles, including environment setup, not only the command Claude ran"

This explicitly answers each of the reporter's three points:

  • How the wrapper receives the command: documented as "a single shell-quoted argument in $1" (not preserved as separate argv entries; the wrapper must re-evaluate it as a shell string).
  • Safe re-exec pattern: documented with the literal example exec bash -c "$1".
  • Failure mode for stdio MCP servers: explicitly warned about with the npx -y <package> example, which is the same class of corruption fixed in v2.1.128 (per https://code.claude.com/docs/en/changelog, line 954: "Fixed MCP stdio servers receiving corrupted arguments when CLAUDE_CODE_SHELL_PREFIX is set and an argument contains spaces or shell metacharacters").

Verdict: The argument-forwarding contract is now documented. The current guidance is the correct one (use exec bash -c "$1"); the reporter's suggested exec "$@" pattern would not match the actual implementation, which deliberately forwards the command line as a single shell-quoted argument rather than as preserved argv.

Claim 2: stdio MCP configuration and exact-argument matching are not cross-referenced

Status: RESOLVED

The stdio MCP section at https://code.claude.com/docs/en/mcp, line 108, still shows the basic syntax:

``bash # Basic syntax claude mcp add [options] <name> -- <command> [args...] ``

with the -- separator explanation on lines 115-128 covering how arguments are passed untouched to the server.

The exact-argument matching rule cited by the reporter is now in the managed-MCP page at https://code.claude.com/docs/en/managed-mcp, line 166:

"Commands match exactly. Every argument, in order. ["npx", "-y", "server"] does not match ["npx", "server"] or ["npx", "-y", "server", "--flag"]."

The stdio server startup contract is documented in mcp.md, and the corresponding allowlist/denylist matching rule is in managed-mcp.md. The env-vars page now bridges both by warning that "treating $1 as a bare executable path breaks stdio MCP servers."

Verdict: The configuration syntax and exact-argument matching rules are documented on their respective canonical pages. The CLAUDE_CODE_SHELL_PREFIX entry now explicitly warns about the stdio MCP failure mode, which is the substantive cross-link the reporter asked for.

---

Current State

| Claim | Status |
|-------|--------|
| Argument-forwarding contract for stdio MCP startup commands is documented | RESOLVED |
| Safe wrapper re-exec pattern is documented | RESOLVED |
| Warning that rebuilding the command can break MCP startup is documented | RESOLVED |
| Exact-argument matching rule for stdio servers is documented | RESOLVED |

---

Suggested Fix

N/A — issue has been resolved. The CLAUDE_CODE_SHELL_PREFIX entry in env-vars.md now documents the wrapper contract, the safe re-exec pattern (exec bash -c "$1"), and the stdio MCP failure mode. The relevant exact-match rule is documented in managed-mcp.md.

---

References

---

Conclusion & Recommendation

Primary Concern: The CLAUDE_CODE_SHELL_PREFIX docs did not explain the argument-forwarding contract for stdio MCP server startup commands.

Resolution Status: RESOLVED

Recommendation: CLOSE

The current CLAUDE_CODE_SHELL_PREFIX entry at https://code.claude.com/docs/en/env-vars, line 248, documents the wrapper's $1 contract, the safe exec bash -c "$1" re-exec pattern, and an explicit warning that treating $1 as a bare executable path breaks stdio MCP servers (the same class of bug fixed in v2.1.128). The substantive content the reporter asked for is now present in the authoritative location for the variable.

github-actions[bot] · 2 months ago

This issue has been automatically locked since it was closed and has not had any activity for 7 days. If you're experiencing a similar issue, please file a new issue and reference this one if it's relevant.