This document describes the specialized build system for the shell-tool-mcp package, which provides patched shell binaries (Bash and Zsh) and supporting Rust utilities for Model Context Protocol (MCP) servers that need to intercept and observe shell command execution. This system builds platform-specific shell variants with exec-wrapper patches and packages them for distribution via npm.
For the main Codex release pipeline that builds the primary codex binary, see Release Pipeline. For MCP configuration and connection management within Codex, see MCP Server Configuration and MCP Connection Manager.
The shell-tool-mcp build system is a separate workflow (.github/workflows/shell-tool-mcp.yml1-677) that produces an npm package containing:
codex-exec-mcp-server and codex-execve-wrapper for multiple targetsThe workflow is invoked as a reusable workflow from the main release pipeline (.github/workflows/rust-release.yml365-372) and operates independently to build cross-platform shell binaries in isolated container environments.
Sources: .github/workflows/shell-tool-mcp.yml1-20 .github/workflows/rust-release.yml365-372
The workflow executes multiple parallel build jobs, each producing artifacts that are later assembled into a unified vendor directory structure. The metadata job determines version information and publishing settings, while package aggregates all artifacts and publish conditionally pushes to npm.
Sources: .github/workflows/shell-tool-mcp.yml23-69 .github/workflows/shell-tool-mcp.yml536-677
The metadata job (.github/workflows/shell-tool-mcp.yml24-68) computes the release version and publishing settings from workflow inputs or the Git tag:
| Input/Output | Description | Example |
|---|---|---|
release-version | Optional version override | 0.74.0 |
release-tag | Git tag to download artifacts from | rust-v0.74.0 |
version | Computed version (output) | 0.74.0 |
npm_tag | npm dist-tag (output) | "" (stable) or "alpha" |
should_publish | Whether to publish (output) | true for stable or x.y.z-alpha.N |
The version resolution logic (.github/workflows/shell-tool-mcp.yml32-68):
release-tag if it matches rust-v*should_publish=true for versions matching x.y.z or x.y.z-alpha.Nnpm_tag="alpha" for alpha releases, empty string for stableSources: .github/workflows/shell-tool-mcp.yml24-68
The rust-binaries job (.github/workflows/shell-tool-mcp.yml70-190) builds two Rust binaries for four targets:
Sources: .github/workflows/shell-tool-mcp.yml70-94
For musl targets, the workflow installs musl build tools and Zig using the same script as the main release pipeline:
The actual cargo build (.github/workflows/shell-tool-mcp.yml176-177):
Artifacts are staged to vendor/$target/ (.github/workflows/shell-tool-mcp.yml179-189).
Sources: .github/workflows/shell-tool-mcp.yml70-190 .github/scripts/install-musl-build-tools.sh1-280
The bash-linux job (.github/workflows/shell-tool-mcp.yml192-288) builds patched Bash in Docker containers for maximum compatibility. The build matrix includes:
| Variant | Image | Architectures |
|---|---|---|
| ubuntu-24.04 | ubuntu:24.04 | x86_64, aarch64 |
| ubuntu-22.04 | ubuntu:22.04 | x86_64, aarch64 |
| ubuntu-20.04 | ubuntu:20.04 | aarch64 |
| debian-12 | debian:12 | x86_64, aarch64 |
| debian-11 | debian:11 | x86_64, aarch64 |
| centos-9 | quay.io/centos/centos:stream9 | x86_64, aarch64 |
Sources: .github/workflows/shell-tool-mcp.yml199-246
The critical steps (.github/workflows/shell-tool-mcp.yml267-282):
a8a1c2fac029404d3f42cd39f5a20f24b6e4fe4bshell-tool-mcp/patches/bash-exec-wrapper.patch./configure --without-bash-malloc (avoids malloc issues)make -j${cores} (parallel compilation)vendor/$target/bash/$variant/bashSources: .github/workflows/shell-tool-mcp.yml248-288
The bash-darwin job (.github/workflows/shell-tool-mcp.yml290-330) follows the same process but runs directly on macOS runners without containers:
| Variant | Runner | Target |
|---|---|---|
| macos-15 | macos-15-xlarge | aarch64-apple-darwin |
| macos-14 | macos-14 | aarch64-apple-darwin |
The build process is identical to Linux (.github/workflows/shell-tool-mcp.yml309-324), using the same bash fork and patch.
Sources: .github/workflows/shell-tool-mcp.yml290-330
The zsh-linux job (.github/workflows/shell-tool-mcp.yml332-456) builds patched Zsh for the same variants as Bash:
Sources: .github/workflows/shell-tool-mcp.yml332-387
The build steps (.github/workflows/shell-tool-mcp.yml407-422):
https://git.code.sf.net/p/zsh/code77045ef899e53b9598bebc5a41db93a548a40ca6shell-tool-mcp/patches/zsh-exec-wrapper.patch./Util/preconfig (generates configure script)./configuremake -j${cores}vendor/$target/zsh/$variant/zshAfter building, the workflow runs a smoke test (.github/workflows/shell-tool-mcp.yml424-450) to verify the exec wrapper functionality:
This test confirms that:
EXEC_WRAPPER environment variable/bin/echo)Sources: .github/workflows/shell-tool-mcp.yml407-456
The zsh-darwin job (.github/workflows/shell-tool-mcp.yml458-534) mirrors the Linux process, building for:
| Variant | Runner | Target |
|---|---|---|
| macos-15 | macos-15-xlarge | aarch64-apple-darwin |
| macos-14 | macos-14 | aarch64-apple-darwin |
The smoke test (.github/workflows/shell-tool-mcp.yml502-528) is identical to Linux, ensuring consistent wrapper behavior across platforms.
Sources: .github/workflows/shell-tool-mcp.yml458-534
After all build jobs complete, the package job (.github/workflows/shell-tool-mcp.yml536-635) assembles artifacts into a unified vendor directory:
vendor/
├── x86_64-unknown-linux-musl/
│ ├── codex-exec-mcp-server
│ ├── codex-execve-wrapper
│ ├── bash/
│ │ ├── ubuntu-24.04/bash
│ │ ├── ubuntu-22.04/bash
│ │ ├── debian-12/bash
│ │ ├── debian-11/bash
│ │ └── centos-9/bash
│ └── zsh/
│ ├── ubuntu-24.04/zsh
│ ├── ubuntu-22.04/zsh
│ ├── debian-12/zsh
│ ├── debian-11/zsh
│ └── centos-9/zsh
├── aarch64-unknown-linux-musl/
│ ├── (same structure as x86_64)
│ └── bash/ubuntu-20.04/bash
├── x86_64-apple-darwin/
│ ├── codex-exec-mcp-server
│ ├── codex-execve-wrapper
│ ├── bash/
│ │ ├── macos-15/bash
│ │ └── macos-14/bash
│ └── zsh/
│ ├── macos-15/zsh
│ └── macos-14/zsh
└── aarch64-apple-darwin/
└── (same structure as x86_64-apple-darwin)
The assembly logic (.github/workflows/shell-tool-mcp.yml573-593):
This merges all per-job artifact uploads into a single unified vendor/ tree.
Sources: .github/workflows/shell-tool-mcp.yml573-607
The package job creates the final npm package by combining:
The staging process (.github/workflows/shell-tool-mcp.yml573-607):
Before packing, all binaries are made executable (.github/workflows/shell-tool-mcp.yml611-619):
The final tarball is created using npm pack (.github/workflows/shell-tool-mcp.yml621-629):
The tarball is uploaded as codex-shell-tool-mcp-npm (.github/workflows/shell-tool-mcp.yml631-635).
Sources: .github/workflows/shell-tool-mcp.yml536-635
The publish job (.github/workflows/shell-tool-mcp.yml637-676) conditionally publishes the package when:
inputs.publish is true (set by rust-release.yml)should_publish is true (stable x.y.z or alpha x.y.z-alpha.N)The workflow uses npm's OIDC-based trusted publishing (.github/workflows/shell-tool-mcp.yml644-657):
| Configuration | Value |
|---|---|
| Permissions | id-token: write, contents: read |
| Node version | 22 |
| Registry URL | https://registry.npmjs.org |
| Scope | @openai |
| npm CLI version | ≥11.5.1 (trusted publishing requirement) |
The publish command (.github/workflows/shell-tool-mcp.yml665-676):
For stable releases (x.y.z), no --tag is passed, publishing to the latest dist-tag. For alpha releases (x.y.z-alpha.N), --tag alpha is used.
Sources: .github/workflows/shell-tool-mcp.yml637-676
The shell-tool-mcp workflow is invoked from rust-release.yml (.github/workflows/rust-release.yml365-372):
This ensures shell-tool-mcp artifacts are built and published alongside every Codex release, maintaining version alignment between the core CLI and the shell tools MCP package.
Sources: .github/workflows/rust-release.yml365-372
The patched shells intercept exec() system calls via an environment variable-based wrapper:
This mechanism allows MCP servers to observe all commands executed within a shell session, enabling features like:
The smoke tests (.github/workflows/shell-tool-mcp.yml424-450 .github/workflows/shell-tool-mcp.yml502-528) verify this behavior works correctly across all built shell variants.
Sources: .github/workflows/shell-tool-mcp.yml424-450 .github/workflows/shell-tool-mcp.yml502-528
Refresh this wiki