A drop-in shell MCP that auto-yields long-running commands into managed background processes.
Project description
YieldShell MCP
A drop-in shell MCP server that auto-yields long-running commands into managed background processes.
Why Auto-Yielding?
Most shell tools present a frustrating choice: either block the LLM agent until the command finishes, or force the agent to decide upfront that a command should run in the background.
YieldShell MCP solves this by keeping normal foreground semantics for fast commands, then automatically promoting long-running commands into managed background processes after a brief delay (yield_ms, default: 1 second).
graph TD
A[exec_command] --> B["Wait for yield_ms (default: 1s)"]
B --> C{Is process still running?}
C -->|Yes| D["backgrounded<br>Returns process_id"]
C -->|No| E["completed<br>Returns full output"]
- Fast Commands (e.g.,
echo hello,ls): Complete instantly, returning the output immediately. - Long-Running Commands (e.g.,
npm run dev,docker build,sleep 60): Automatically yield control back to the agent with aprocess_idand a snapshot of initial output, letting the agent decide when toread,wait, orstopthe process.
Installation
From Registry (Recommended)
To run the published package via uv:
uv tool install mcp-yieldshell
Local Development
To clone and run locally:
git clone <repo-url> && cd mcp-yieldshell
uv sync
uv run mcp-yieldshell
MCP Client Configuration
Claude Desktop
To configure the server in Claude Desktop, add the configuration below to your Claude Desktop config file:
- macOS:
~/Library/Application Support/Claude/claude_desktop_config.json - Windows:
%APPDATA%\Claude\claude_desktop_config.json
Production (via uvx)
{
"mcpServers": {
"yieldshell": {
"command": "uvx",
"args": ["mcp-yieldshell"]
}
}
}
Production with Security Restrictions
{
"mcpServers": {
"yieldshell": {
"command": "uvx",
"args": ["mcp-yieldshell"],
"env": {
"YIELDSHELL_ALLOWED_CWDS": "/home/user/projects:/tmp/build",
"YIELDSHELL_DEFAULT_TIMEOUT_MS": "300000"
}
}
}
}
Local Development Setup
Replace /path/to/mcp-yieldshell with the absolute path to your cloned repository:
{
"mcpServers": {
"yieldshell": {
"command": "uv",
"args": [
"--directory",
"/path/to/mcp-yieldshell",
"run",
"mcp-yieldshell"
]
}
}
}
Cursor
To configure the server in Cursor:
- Open Cursor Settings -> Features -> MCP.
- Click + Add New MCP Server.
- Fill out the form:
- Name:
yieldshell - Type:
stdio - Command:
uvx mcp-yieldshell(oruv --directory /path/to/mcp-yieldshell run mcp-yieldshellfor local development)
- Name:
OpenCode
Add to your OpenCode MCP settings:
{
"mcpServers": {
"yieldshell": {
"command": "uvx",
"args": ["mcp-yieldshell"]
}
}
}
Tool Reference
exec
Execute a shell command. If the command runs longer than yield_ms, it yields a process_id and runs in the background.
-
Parameters:
command(string, required): The command string to execute in the shell.cwd(string, optional): Working directory for the command. Must be under allowed roots ifYIELDSHELL_ALLOWED_CWDSis set. Defaults toYIELDSHELL_DEFAULT_CWD.env(object of string to string, optional): Additive environment variable overlay. Merged into the parent environment.shell(string, optional): A custom shell path to execute commands (e.g./bin/zsh). Internally executed using the platform's default shell handler if omitted.stdin(string, optional): Initial text input written to standard input immediately after spawning.name(string, optional): A human-readable label/name to identify this process.yield_ms(integer, optional): Milliseconds to wait before yielding execution to background. Clamped byYIELDSHELL_MAX_YIELD_MS. Defaults toYIELDSHELL_DEFAULT_YIELD_MS(5000ms).timeout_ms(integer, optional): Total execution runtime limit in milliseconds. Process is killed if it runs longer than this. Defaults toYIELDSHELL_DEFAULT_TIMEOUT_MS(0 = no limit).max_output_bytes(integer, optional): Maximum output bytes to capture in stdout/stderr ring buffers. Subject toYIELDSHELL_MAX_OUTPUT_BYTEScap.
-
Output Statuses:
completed: Process finished withinyield_ms. Returns exit code, stdout, and stderr.backgrounded: Process auto-yielded. Returnsprocess_idandpidfor tracking.timed_out: Process exceededtimeout_msand was terminated.stopped: Process was explicitly terminated.failed_to_start: Command could not be spawned (e.g., bad directory or policy violation).failed: An internal execution error occurred.
read
Read stdout and/or stderr output from a running or completed background process.
-
Parameters:
process_id(string, required): Unique identifier of the process.since_seq(integer, optional): Return only output appended after this sequence number. Enables efficient incremental log polling.max_output_bytes(integer, optional): Clamps the response size. Defaults to the server cap.streams(string, default:"both"): The streams to read. Options:"both","stdout", or"stderr".
-
Returns:
process_id,status,exit_code,signal,next_seq(sequence index to use in subsequentsince_seqreads),stdout/stderrtext, and atruncatedflag.
write
Write text input to the standard input (stdin) of a running process.
- Parameters:
process_id(string, required): Unique identifier of the process.input(string, required): Text input to write.newline(boolean, default:false): Iftrue, appends\nto the input.
wait
Block execution until the process exits or the wait timeout expires. This allows the LLM to pause and await completion without spawning a new execution loop.
-
Parameters:
process_id(string, required): Unique identifier of the process.timeout_ms(integer, default:30000): Maximum time to wait.max_output_bytes(integer, optional): Maximum output bytes to return in the response.
-
Important: If the wait timeout expires,
waitreturns the current status but does not kill the process. It continues running in the background.
stop
Gracefully terminate or force kill a running process.
- Parameters:
process_id(string, required): Unique identifier of the process.signal(string, default:"SIGTERM"): OS signal to send (e.g.SIGTERM,SIGKILL,SIGINT). Ignored on Windows.force_after_ms(integer, default:3000): Grace period before escalating to force kill (SIGKILL).
ps
List all managed processes.
- Parameters:
include_completed(boolean, default:true): Iffalse, finished/stopped processes are excluded from the output.limit(integer, default:50): Maximum number of entries.
cleanup
Prune completed or stopped process records to free memory.
- Parameters:
completed_older_than_ms(integer, default:3600000): Prunes completed processes older than this threshold (1 hour default).stopped_older_than_ms(integer, default:3600000): Prunes stopped, timed-out, or failed processes older than this threshold (1 hour default).
Sequence Number & Incremental Reads
To avoid sending duplicate data over the MCP protocol (which can consume context window space), the server implements a sequence-based polling protocol:
- Every output chunk appended to a process's ring buffer receives a unique, incremental sequence number (
seq). - When calling
exec,read, orwait, the response includes anext_seqvalue representing the index of the next chunk to be written. - To retrieve only new output, call
readwithsince_seqset to the previously receivednext_seq. - Omitting
since_seqreturns the entire contents currently stored in the buffer (clamped bymax_output_bytes).
Configuration Variables
Configure the server by setting these environment variables prior to launch:
| Environment Variable | Default Value | Description |
|---|---|---|
YIELDSHELL_DEFAULT_CWD |
Current directory | The fallback working directory for commands. |
YIELDSHELL_ALLOWED_CWDS |
(none) | A list of allowed directory paths separated by os.pathsep (e.g., : on UNIX, ; on Windows). If set, all command execution paths must resolve inside one of these roots. |
YIELDSHELL_MAX_OUTPUT_BYTES |
20000 |
The default and maximum capacity of the ring buffers for stdout/stderr. |
YIELDSHELL_MAX_PROCESSES |
50 |
Maximum concurrent managed processes. Spawning a new command when this limit is reached will return failed_to_start. |
YIELDSHELL_DEFAULT_YIELD_MS |
5000 |
Fallback delay before auto-yielding. |
YIELDSHELL_MAX_YIELD_MS |
300000 |
The maximum allowed value for the yield_ms parameter. |
YIELDSHELL_DEFAULT_TIMEOUT_MS |
0 |
Default hard runtime limit (0 means no limit). |
YIELDSHELL_DENY_COMMAND_REGEX |
(none) | A regular expression pattern. Commands matching this pattern are blocked before starting. |
YIELDSHELL_ALLOW_COMMAND_REGEX |
(none) | A regular expression pattern. If set, only commands matching this pattern are permitted. |
YIELDSHELL_REDACT_ENV_REGEX |
TOKEN|KEY|SECRET|PASSWORD |
Regex to identify sensitive environment variable keys. Their values are redacted in stdout/stderr outputs. |
Security Notes
- Arbitrary Code Execution: This server executes shell commands on the host system. Always run the server inside a container, sandbox, or isolated development VM.
- Path Validation: CWD path verification uses absolute paths (
resolve()), preventing path-traversal attacks (../) outside the allowed roots. - Additive Environments: The
envargument overlays existing env parameters. It merges with the parent process environment instead of completely replacing it, protecting critical OS vars. - Best-effort Redaction: While values of variables matching
YIELDSHELL_REDACT_ENV_REGEXare scrubbed from outputs, this is a best-effort system. Sensitive data printed through complex formats or argument lists might not be caught.
Platform Support
- POSIX (Linux & macOS): Fully supported. Spawns processes in distinct sessions (
start_new_session=True), allowing signals (SIGTERM/SIGKILL) to target the entire process group. This ensures child processes started by commands (such as npm dev tasks) are completely cleaned up. - Windows: Supported with best-effort process group controls. Windows lacks native POSIX signals, meaning
stopandtimeout_msact on the primary process, and child subprocesses might persist if they do not exit cleanly.
License
MIT
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file mcp_yieldshell-0.1.8.tar.gz.
File metadata
- Download URL: mcp_yieldshell-0.1.8.tar.gz
- Upload date:
- Size: 19.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fbaf6bae48b2434d37e6acda5c1c72ea69c6bdc3d18a182d9bde67a634f6a872
|
|
| MD5 |
c42f2b70b8b0359540506952c080c08f
|
|
| BLAKE2b-256 |
86673a1d109bb1171a445649269a95f4b8a4e7f3759448d324bcaa2c8b67795a
|
Provenance
The following attestation bundles were made for mcp_yieldshell-0.1.8.tar.gz:
Publisher:
publish.yml on crzidea/mcp-yieldshell
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
mcp_yieldshell-0.1.8.tar.gz -
Subject digest:
fbaf6bae48b2434d37e6acda5c1c72ea69c6bdc3d18a182d9bde67a634f6a872 - Sigstore transparency entry: 1673398204
- Sigstore integration time:
-
Permalink:
crzidea/mcp-yieldshell@d48083502565529eea64cced3389917bbcdbae10 -
Branch / Tag:
refs/tags/v0.1.8 - Owner: https://github.com/crzidea
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@d48083502565529eea64cced3389917bbcdbae10 -
Trigger Event:
push
-
Statement type:
File details
Details for the file mcp_yieldshell-0.1.8-py3-none-any.whl.
File metadata
- Download URL: mcp_yieldshell-0.1.8-py3-none-any.whl
- Upload date:
- Size: 16.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
baea5250c5675f5b2625cd7bf3715923a2fdb3de5a823f4973a915e43add4414
|
|
| MD5 |
f8d06d73c6623a9a4930ff6d306f7742
|
|
| BLAKE2b-256 |
99f765e38badc29d5345b9632360f3460c526eda021cf097aa40d4af74acd073
|
Provenance
The following attestation bundles were made for mcp_yieldshell-0.1.8-py3-none-any.whl:
Publisher:
publish.yml on crzidea/mcp-yieldshell
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
mcp_yieldshell-0.1.8-py3-none-any.whl -
Subject digest:
baea5250c5675f5b2625cd7bf3715923a2fdb3de5a823f4973a915e43add4414 - Sigstore transparency entry: 1673398211
- Sigstore integration time:
-
Permalink:
crzidea/mcp-yieldshell@d48083502565529eea64cced3389917bbcdbae10 -
Branch / Tag:
refs/tags/v0.1.8 - Owner: https://github.com/crzidea
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@d48083502565529eea64cced3389917bbcdbae10 -
Trigger Event:
push
-
Statement type: