YieldShell MCP
A drop-in shell MCP server that auto-yields long-running commands into managed background executions.
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 executions after a delay (yield_ms, default: 30 seconds).
graph TD
A[execute] --> B["Wait for yield_ms (default: 30s)"]
B --> C{Is execution still running?}
C -->|Yes| D["backgrounded<br>Returns execution_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 anexecution_idand a snapshot of initial output, letting the agent decide when toread,wait, orstopthe execution.
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)
OpenCode
Add to your OpenCode MCP settings:
{
"mcpServers": {
"yieldshell": {
"command": "uvx",
"args": ["mcp-yieldshell"]
}
}
}
These client command and environment snippets are unchanged in 1.0; the breaking changes affect tool calls and response fields, not server launch configuration.
Tool Reference
execute
Execute a shell command. If it runs longer than yield_ms, the execution moves
to the background. Addressable responses include an opaque execution_id;
clients must pass it through unchanged to lifecycle tools. process_id is the
numeric OS PID of the initial shell and is not a lifecycle handle.
-
Parameters:
command(string, required): The command string to execute in the shell.side_effects(array of string, required): The side-effect categories this command may plausibly have. Must contain at least one entry drawn from the enum below. Use["NONE"]for commands with no meaningful side effects.NONEis exclusive and must not be combined with any other category. The server rejects the call withfailed_to_startif any declared category is configured as blocked.- Allowed values:
CHANGES_NETWORK_CONFIGURATION,CHANGES_PACKAGES_OR_DEPENDENCIES,CONSUMES_SIGNIFICANT_RESOURCES,DELETES_FILES,EXPOSES_SECRETS,KILLS_AGENT_PROCESS,MAKES_NETWORK_REQUESTS,MODIFIES_OS_SETTINGS,MODIFIES_OS_USER_SETTINGS,MODIFIES_OUTSIDE_WORKSPACE,MODIFIES_PRODUCTION_SERVICES,MODIFIES_PROTECTED_FILES,MODIFIES_SECURITY_CONTROLS,MODIFIES_WORKSPACE_FILES,NONE,OTHER,RUNS_INLINE_CODE,RUNS_PRIVILEGED_COMMANDS,STOPS_OR_RESTARTS_SERVICES,UNKNOWN,USES_DESTRUCTIVE_GIT_OPERATION. RUNS_INLINE_CODEis in the default blocklist. It covers commands that execute code supplied inline to an interpreter or shell (e.g.python -c,node -e,curl ... | sh). It does not cover simply creating a script or executable file unless the same command also executes inline code. The safer next action is to write the content to a reviewable workspace file and execute it in a small, inspectable step. Operators can clear the blocklist viaMCP_YIELDSHELL_BLOCKED_SIDE_EFFECTS=,.
- Allowed values:
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): Shell executable used to run the command. Defaults to the platform shell. Explicit shells are checked by the same allow/deny command policy.stdin(string, optional): Initial text input written to standard input immediately after spawning.close_stdin(boolean, default:true): Close standard input after the initial input is written. Set tofalsewhen follow-upwritecalls are expected.name(string, optional): A human-readable label for this execution.yield_ms(integer, optional): Milliseconds to wait before yielding execution to background. Both omitted and explicit values are clamped to the lesser ofYIELDSHELL_MAX_YIELD_MSand the transport-safe 55,000ms ceiling. Defaults toYIELDSHELL_DEFAULT_YIELD_MS(30,000ms).timeout_ms(integer, optional): Total execution runtime limit in milliseconds. Process is terminated if it runs longer than this. Defaults toYIELDSHELL_DEFAULT_TIMEOUT_MS(3,600,000ms). Pass0explicitly for unlimited execution.max_output_bytes(integer, optional): Maximum bytes returned in total across both response streams. Subject to theYIELDSHELL_MAX_OUTPUT_BYTEScap. This bounds the response only — retention is controlled separately byYIELDSHELL_MAX_BUFFER_BYTES, so output withheld here stays readable viaread.
-
Side-Effects Guide:
side_effectsis required and must be a non-empty list. Declare every plausible side-effect category before running the command.NONEis exclusive and valid only when no meaningful side effect is expected. Use["NONE"]for read-only commands.- The server rejects the call with
failed_to_startif any declared category is blocked. Rejection messages name each blocked category, state that execution was stopped by policy before the process started, and provide a category-specific safer next action. - Categories are case-sensitive and must use the canonical enum names listed above.
- Discouraged: executing code supplied inline to an interpreter or shell (e.g.
python -c,node -e,ruby -e,perl -e, shell heredocs piped into interpreters, orcurl ... | sh). Agents should prefer writing such content to a reviewable workspace file and executing it in a small, inspectable step with explicit matchingside_effects. DeclaringRUNS_INLINE_CODEis rejected under the default policy.
-
Side-Effect Examples:
- Read-only command:
side_effects=["NONE"] - Workspace write:
side_effects=["MODIFIES_WORKSPACE_FILES"] - Dependency install:
side_effects=["CHANGES_PACKAGES_OR_DEPENDENCIES", "MAKES_NETWORK_REQUESTS"] - Network access:
side_effects=["MAKES_NETWORK_REQUESTS"] - Destructive file operations:
side_effects=["DELETES_FILES"] - Privileged command:
side_effects=["RUNS_PRIVILEGED_COMMANDS"] - Protected-file changes:
side_effects=["MODIFIES_PROTECTED_FILES"] - Inline code execution: prefer writing the content to a reviewable workspace file (for example
scripts/migrate.sqlortools/build.sh) and run it in a small, inspectable step. Declaringside_effects=["RUNS_INLINE_CODE"]is rejected under the default policy; operators can clear that default withMCP_YIELDSHELL_BLOCKED_SIDE_EFFECTS=,.
- Read-only command:
-
Output Statuses:
- Every status returns the cursor fields described under Byte Cursors & Incremental Reads:
start_seq,next_seq,latest_seq,capped, andevicted. The inline snapshot advances the execution's server-side read cursor, so a followingreadorwaitcontinues after it rather than repeating it. - Every retained result (
completed,backgrounded,timed_out,stopped, orfailed) returnsexecution_idand, when available, the initial shell's numericprocess_id. completed: Execution finished withinyield_ms. Returns exit code, stdout, and stderr; retained output remains addressable withexecution_id.backgrounded: Execution auto-yielded. Returns both identifiers, a snapshot of initial stdout/stderr,duration_ms, cursor fields, and a lifecycle-tool hint.timed_out: Execution exceededtimeout_msand its process group was terminated.stopped: Execution was explicitly terminated.failed_to_start: The command could not create a managed execution (for example, because of a bad directory or policy violation). This outcome is not addressable and may omit both identifiers.failed: An internal execution error occurred.- If initial
stdindelivery fails, responses for the retained execution includestdin_errorwith the transport error. Initial delivery runs as tracked background work so pipe backpressure does not delay auto-yielding.
- Every status returns the cursor fields described under Byte Cursors & Incremental Reads:
Example background response (cursor/output fields abbreviated):
{
"status": "backgrounded",
"execution_id": "a97e81c9034f",
"process_id": 42137,
"stdout": "",
"stderr": "",
"next_seq": 1,
"message": "Execution is running in the background. Use read, wait, or stop with execution_id."
}
read
Read stdout and/or stderr from a running or completed execution.
-
Parameters:
execution_id(string, required): Opaque execution handle returned byexecute; pass it through unchanged.since_seq(integer, optional): Byte-position cursor returned asnext_seqby the previous read. Enables lossless incremental log polling. When omitted, the read resumes from the server-side cursor instead (see Byte Cursors & Incremental Reads); passsince_seq=1to re-read from the beginning.max_output_bytes(integer, optional): Clamps total output returned across the selected streams. Defaults to the server cap.streams(string, default:"both"): The streams to read. Options:"both","stdout", or"stderr".tail_lines(integer, optional): Return only the newest N lines instead of reading forward. Useful for monitoring a noisy build or test run without paging through it. Mutually exclusive withsince_seq; must be positive. If the requested tail exceedsmax_output_bytes, the newest bytes are kept rather than the oldest.
-
Returns:
execution_id,status,exit_code,signal, andstdin_errorwhen initial input delivery failed.stdoutandstderrtext are included based on thestreamsfilter —"both"includes both,"stdout"includes onlystdout, and"stderr"includes onlystderr.- Cursor fields:
start_seq(position of the first byte returned),next_seq(cursor to use in the nextsince_seqread), andlatest_seq(how far output has advanced overall — when it exceedsnext_seq, more is waiting). - Withheld-output flags:
cappedmeans more output is available and reading again continues fromnext_seq;evictedmeans output fell out of the ring buffer before it was read and cannot be recovered. When either is set, ahintfield names the concrete follow-up call.
write
Write text input to the standard input (stdin) of a running execution.
- Parameters:
execution_id(string, required): Opaque execution handle returned byexecute; pass it through unchanged.input(string, required): Text input to write.newline(boolean, default:false): Iftrue, appends\nto the input.close_stdin(boolean, default:false): Close standard input after this write, delivering EOF to the process.
- Writes that remain backpressured are capped at the transport-safe 55-second
request ceiling and return
ok: falsewith an error.
wait
Block until an execution exits or the wait timeout expires.
-
Parameters:
execution_id(string, required): Opaque execution handle returned byexecute; pass it through unchanged.timeout_ms(integer, default:55000): Maximum time to wait. Never stops the execution.max_output_bytes(integer, optional): Maximum total output bytes to return across stdout and stderr.since_seq(integer, optional): Byte-position cursor from a previous response. When omitted,waitresumes from the server-side cursor, so polling in a loop returns each byte exactly once without any cursor bookkeeping.tail_lines(integer, optional): Return only the newest N lines. Mutually exclusive withsince_seq.
-
Returns: the same cursor and withheld-output fields as
read, plus:wait_result:"exited"when no live work remains, or"deadline_reached"when the wait budget ran out first. This is what distinguishes a still-running process from a wait that gave up —statusalone cannot.waited_ms: time actually spent waiting.max_wait_ms: the effective budget after capping, so a clamped request is visible rather than silent.
-
Important: If the wait timeout expires,
waitreturns the current status but does not stop the execution. It continues running in the background. -
The effective wait duration is capped at 55 seconds to stay well under typical MCP request timeouts, even if a larger
timeout_msis requested. The response reports that cap inmax_wait_ms. -
waittreats the managed process group disappearing as completion. If the tracked shell exits while descendants in its process group remain alive, the record continues to reportrunning. For normal process-group completion, stdout/stderr are drained before the response is returned. Final drain waits are bounded so inherited pipes cannot block a request indefinitely.
Timing Parameters at a Glance
Three separate timers control different things. Only one of them ever terminates a process:
| Parameter | Meaning | Terminates the process? | Default | Effective cap |
|---|---|---|---|---|
execute(yield_ms) |
How long execute stays inline before handing back an execution_id and letting the command continue in the background. |
No | 30,000ms | Lesser of YIELDSHELL_MAX_YIELD_MS and 55,000ms |
execute(timeout_ms) |
Total execution limit. On expiry the process group is sent SIGTERM, then SIGKILL, and the status becomes timed_out. |
Yes | 3,600,000ms | None (0 means unlimited) |
wait(timeout_ms) |
Maximum time a single wait call blocks. On expiry it returns wait_result: "deadline_reached" and the process keeps running. |
No | 55,000ms | 55,000ms |
If wait returns with status: "running", check wait_result: "deadline_reached" means the wait budget expired while the tracked shell or one of its descendants was still alive. The process keeps running and can be polled again.
stop
Gracefully terminate or force kill a running execution.
- Parameters:
execution_id(string, required): Opaque execution handle returned byexecute; pass it through unchanged.signal(string, default:"SIGTERM"): OS signal to send (e.g.SIGTERM,SIGKILL,SIGINT). Invalid names are rejected without stopping the process. Valid names are ignored on Windows.force_after_ms(integer, default:10000): Grace period before escalating to force kill (SIGKILL). It is clamped below the transport-safe 55-second request ceiling so force-kill observation, final output drain, and subprocess reaping still fit within the request.
ps
List managed executions, including retained terminal records.
Terminal records are retained temporarily for inspection. Before each otherwise valid execute spawn, records older than YIELDSHELL_PROCESS_RETENTION_MS are removed and the remaining terminal set is reduced to YIELDSHELL_MAX_RETAINED_PROCESSES, oldest first. Running records are never automatically removed. If the shell exits but its process group still has live descendants, status remains running until descendants stop. Reaped IDs disappear from ps and become unknown to read, wait, write, and stop.
- Parameters:
include_completed(boolean, default:true): Iffalse, terminal executions are excluded.limit(integer, default:50): Maximum number of entries.
- Returns:
executions— a list of execution summaries. Each contains opaqueexecution_id, numeric OSprocess_id(ornullif unavailable),name,command,cwd,status,exit_code,signal,started_at,ended_at,duration_ms,stdout_bytes,stderr_bytes, andstdin_error. - Activity fields help distinguish a genuine hang from quiet work:
last_output_at: wall-clock time output last arrived on either stream, ornullif none has.idle_ms: milliseconds since that moment, ornullif no output has arrived. A large and growingidle_mson arunningprocess is the signal that it may be stuck.latest_seq: the execution-wide cursor position just past the newest captured byte. Comparing it to anext_seqyou hold shows how much output is waiting.
{
"executions": [
{
"execution_id": "a97e81c9034f",
"process_id": 42137,
"status": "running",
"idle_ms": 125.4,
"latest_seq": 2049
}
]
}
Error Responses
All tools that accept an execution_id return a structured error when the
opaque handle is unknown, for example:
{"execution_id": "a97e81c9034f", "error": "Unknown execution_id: a97e81c9034f"}.
The response echoes execution_id; a numeric process_id is not accepted as
a substitute.
cleanup
Prune completed, stopped, timed-out, and failed execution records.
- Parameters:
completed_older_than_ms(non-negative integer, default:3600000): Prunes completed executions older than this threshold (1 hour default).stopped_older_than_ms(non-negative integer, default:3600000): Prunes stopped, timed-out, or failed executions older than this threshold (1 hour default).
- Returns:
removed— the count of execution records pruned. Negative thresholds are rejected without removing records and include anerrormessage.
Byte Cursors & Incremental Reads
To avoid sending duplicate data over the MCP protocol (which can consume context window space), the server implements a byte-position polling protocol:
- Every stdout/stderr byte receives a unique position in a cursor shared by both streams. Positions start at 1 and only ever increase.
read,wait, andexecuteall returnnext_seq, the position immediately after the range covered by the response. A response cap can therefore end safely inside a drain chunk.- You do not have to track cursors. A
readorwaitwith nosince_seqand notail_linesresumes from where the last such call stopped, then advances a server-side cursor held per execution. Polling in a loop therefore returns each byte exactly once.execute's inline snapshot advances the same cursor, so a followingwaitcontinues after it. - To drive the cursor yourself instead, pass the previous
next_seqback assince_seqand keep the samestreamsselection. - A cursorless read consumes. Once output has been delivered, a later cursorless read will not return it again. To inspect output that was already delivered, pass
since_seq=1to re-read from the beginning, or usetail_lines. - Requests that pass
since_seq, passtail_lines, or narrowstreamsare treated as out-of-band: they neither consult nor advance the server-side cursor. This means a peek at the tail or at stderr cannot silently skip output the polling stream has not seen yet. - Two distinct flags report output the response did not carry, and they call for different responses:
capped— the response cap stopped the read short. The remainder is still buffered; read again to continue, or pass the returnednext_seqassince_seq. Thehintfield spells out the call.evicted— output exceeded the ring buffer's capacity before it was read and is permanently gone. Retrying cannot recover it; poll more often or raiseYIELDSHELL_MAX_BUFFER_BYTES. Reads report eviction only when their requested range overlaps evicted data.
latest_seqreports how far output has advanced overall.latest_seq > next_seqmeans more is already waiting;latest_seq == next_seqmeans the caller is fully caught up.- Because retention (
YIELDSHELL_MAX_BUFFER_BYTES, 256 KB per stream) is much larger than the per-response cap (YIELDSHELL_MAX_OUTPUT_BYTES, 20 KB), a cursor normally stays resolvable across polling gaps.
Cursor boundaries preserve valid UTF-8 characters. If a single character is larger than a very small requested page, that page may exceed max_output_bytes by at most three bytes so the cursor can make progress without corrupting the character.
Positions are never reused and never reset. Eviction does not rewind them: it advances the oldest retained position while next_seq keeps climbing, so a cursor is safe to hold indefinitely. A cursor pointing at data that has since been evicted is not an error — the read returns data from the earliest retained position onward and sets evicted to true, and start_seq shows where the returned range actually begins. Positions are scoped to one execution; a cursor from one execution_id is meaningless for another.
Incremental cursors are scoped to the selected streams value. Keep the same stream selection while advancing a cursor; switching from stdout-only or stderr-only polling to another selection can intentionally skip bytes from the previously unselected stream.
When the initial shell exits normally, execute/wait responses include output drained through stdout/stderr EOF. If a descendant keeps inherited stdout/stderr open, the server stops waiting on those inherited pipes to avoid indefinite blocking; output written only by that descendant after the shell exits is not part of the managed execution result.
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 bytes any single response may return across stdout and stderr. This is a response cap only; it does not limit what is retained. Nonpositive or nonnumeric values use the default. |
YIELDSHELL_MAX_BUFFER_BYTES |
262144 |
Capacity of each per-execution stdout/stderr ring buffer. Retention is deliberately larger than one response so since_seq cursors and tail_lines still resolve after a gap between polls. Raise it for very chatty commands, at a cost of roughly this value × 2 streams × live executions in memory. Nonpositive or nonnumeric values use the default. |
YIELDSHELL_MAX_PROCESSES |
50 |
Maximum concurrent live managed process groups, including descendants that outlive a completed shell. Spawning a new command when this limit is reached returns failed_to_start. Nonpositive or nonnumeric values use the default. |
YIELDSHELL_DEFAULT_YIELD_MS |
30000 |
Fallback delay before auto-yielding. Effective yields are also capped at 55,000ms. |
YIELDSHELL_MAX_YIELD_MS |
300000 |
Configured maximum for yield_ms; the effective maximum is the lesser of this value and 55,000ms. |
YIELDSHELL_DEFAULT_TIMEOUT_MS |
3600000 |
Default hard runtime limit (1 hour). An explicit tool argument of 0 means no limit. |
YIELDSHELL_PROCESS_RETENTION_MS |
3600000 |
Age after which terminal execution records are reaped before a valid spawn. Zero requests immediate age-based reaping. Negative or nonnumeric values use the default. |
YIELDSHELL_MAX_RETAINED_PROCESSES |
100 |
Maximum retained terminal records after age reaping; oldest records are removed first. Zero retains no prior terminal records. Negative or nonnumeric values use the default. Running records are excluded. |
YIELDSHELL_DENY_COMMAND_REGEX |
(none) | A regular expression pattern. Commands matching this pattern are blocked before starting. Invalid patterns cause startup to fail with a configuration error naming this variable. |
YIELDSHELL_ALLOW_COMMAND_REGEX |
(none) | A regular expression pattern. If set, only commands matching this pattern are permitted. Invalid patterns cause startup to fail with a configuration error naming this variable. |
YIELDSHELL_REDACT_ENV_REGEX |
(none) | Optional regex identifying sensitive environment variable names. When configured, matching non-empty values of at least 8 characters are snapshotted at startup and redacted in stdout/stderr outputs. Invalid patterns cause startup to fail with a configuration error naming this variable. |
MCP_YIELDSHELL_BLOCKED_SIDE_EFFECTS |
KILLS_AGENT_PROCESS,MODIFIES_OS_SETTINGS,MODIFIES_OS_USER_SETTINGS,MODIFIES_PROTECTED_FILES,RUNS_INLINE_CODE |
Comma-separated list of side_effects enum names the server should reject. Names are case-sensitive. Surrounding whitespace is trimmed and empty entries are ignored. Invalid names cause startup to fail. Set to , (or any value that resolves to no entries) to clear the default blocklist. |
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.
- Side-Effect Declarations: Every
executecall must declare its plausible side-effect categories viaside_effects. By default,KILLS_AGENT_PROCESS,MODIFIES_OS_SETTINGS,MODIFIES_OS_USER_SETTINGS,MODIFIES_PROTECTED_FILES, andRUNS_INLINE_CODEare blocked. Operators can adjust the blocklist viaMCP_YIELDSHELL_BLOCKED_SIDE_EFFECTS(including cleared to,to disable every default). This is an explicit risk signal — it is not a complete sandbox, and LLM under-declaration remains possible. - Inline Code Execution: The
RUNS_INLINE_CODEdefault discourages agents from executing code supplied inline to an interpreter or shell (e.g.python -c,node -e,ruby -e,perl -e, shell heredocs piped into interpreters, orcurl ... | sh). The safer pattern is to write the content to a reviewable workspace file and execute it in a small, inspectable step with explicit matchingside_effects. Operators can override the default to permit the category. - OS User Settings Damage:
MODIFIES_OS_USER_SETTINGScovers commands that change user-level configuration such as shell rc files, XDG config directories, dotfiles, or per-user application preferences. This is distinct fromMODIFIES_OS_SETTINGS, which covers broader OS-level configuration such as systemd units, kernel parameters,/etcfiles, and package manager system config. Blocked by default; operators can override. - Agent Process Termination:
KILLS_AGENT_PROCESScovers commands that may terminate the MCP client, agent, or related process running the agent workflow (e.g.,killcommands targeting the agent PID, or commands that cause the agent to exit). This is distinct fromSTOPS_OR_RESTARTS_SERVICES, which covers OS-level services. Blocked by default; operators can override. - Path Validation: CWD path verification resolves traversal and symlinks, then requires the target to be an allowed root itself or a true descendant. Lexically similar siblings and symlink escapes are rejected before spawn.
- Additive Environments: The
envargument overlays existing env parameters. It merges with the parent process environment instead of completely replacing it, protecting critical OS vars. - Opt-in Best-effort Redaction: Redaction is disabled unless
YIELDSHELL_REDACT_ENV_REGEXis configured. When enabled, matching environment values are snapshotted at startup, ordered longest first, and values shorter than 8 characters are excluded to avoid corrupting ordinary output. Matching values supplied through anenvoverlay are added for that process. Redaction is applied while streams are drained, including across subprocess chunks and incremental read pages. Restart the server to refresh the parent-environment snapshot after changes. Redaction does not remove variables from subprocess environments, and secrets not selected by the regex or printed through transformed formats might not be caught.
Lifecycle and Compatibility
YieldShell gives graceful termination 10 seconds before force-killing, waits up to 5 seconds for a POSIX process group to disappear, and allows up to 3 seconds for final stream draining. On stdio server shutdown, all live managed executions are terminated concurrently using the same bounded graceful/forced policy. Already-terminal records are not changed by shutdown.
execution_id is an opaque, server-generated string. Clients must pass it
through unchanged rather than parsing or pattern-matching it. Its current
shape is an implementation detail, not a client contract.
Current lifecycle defaults are a 30-second auto-yield, one-hour runtime timeout, 55-second effective request-wait and stop-grace ceilings, 10-second default stop grace, automatic terminal-record retention, opt-in streaming output redaction, and shutdown containment. Standard input closes after execute by default; callers that need an interactive session must set close_stdin=false. Use explicit shorter timing values where lower latency is preferred, and pass timeout_ms=0 to retain unlimited runtime behavior.
Migration (0.x → 1.0)
Version 1.0.0 is a clean breaking release. There are no compatibility aliases or dual-emitted legacy fields.
| 0.x API | 1.0 API |
|---|---|
Tool exec |
Tool execute |
Managed handle process_id (string) |
Managed handle execution_id (opaque string) |
OS field pid |
OS field process_id (integer) |
ps response key processes |
ps response key executions |
Assumed ID pattern ^proc_[0-9a-f]{12}$ |
No supported pattern; treat execution_id as opaque |
The most important migration trap is that process_id still exists but now
means the numeric OS PID. Store execution_id from execute and pass it
unchanged to read, write, wait, and stop. Calls that send the OS
process_id as the handle fail with Unknown execution_id.
Platform Support
- POSIX (Linux & macOS): Fully supported. Spawns processes in distinct sessions (
start_new_session=True), allowingstop, runtime timeout, and server shutdown signals (SIGTERM/SIGKILL) to target the entire process group. This cleans up child processes started by managed commands. - Windows: Supported with best-effort process controls. Windows lacks native POSIX process-group signals, so
stop,timeout_ms, and server shutdown act on the primary process; child subprocesses might persist if they do not exit cleanly.
License
MIT
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-1.1.0.tar.gz.
File metadata
- Download URL: mcp_yieldshell-1.1.0.tar.gz
- Upload date:
- Size: 76.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
67cf1a02e285478fb31ca207712dab92c4178fd685252c917160c440392810ed
|
|
| MD5 |
fd2469cec66e460d2327ef5d8b19dadf
|
|
| BLAKE2b-256 |
b20810645c81bd0dafb9afc038ca0065db921ab3c1340d941192b875b20e3505
|
Provenance
The following attestation bundles were made for mcp_yieldshell-1.1.0.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-1.1.0.tar.gz -
Subject digest:
67cf1a02e285478fb31ca207712dab92c4178fd685252c917160c440392810ed - Sigstore transparency entry: 2288174237
- Sigstore integration time:
-
Permalink:
crzidea/mcp-yieldshell@bdcefa4b8515cc19e75d6bf3671afb10dc38d27e -
Branch / Tag:
refs/tags/v1.1.0 - Owner: https://github.com/crzidea
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@bdcefa4b8515cc19e75d6bf3671afb10dc38d27e -
Trigger Event:
push
-
Statement type:
File details
Details for the file mcp_yieldshell-1.1.0-py3-none-any.whl.
File metadata
- Download URL: mcp_yieldshell-1.1.0-py3-none-any.whl
- Upload date:
- Size: 40.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
897dfdc07bc31a4497fe9b22a0b008d446d067e52614b2c9ecdb36d7d1703e02
|
|
| MD5 |
27b1d9ded6fc3a18b40eba4e640b3208
|
|
| BLAKE2b-256 |
c7ec57ec88fcef21d9829398a45c825d313929d8e30912f299fe90f6d1f52fe4
|
Provenance
The following attestation bundles were made for mcp_yieldshell-1.1.0-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-1.1.0-py3-none-any.whl -
Subject digest:
897dfdc07bc31a4497fe9b22a0b008d446d067e52614b2c9ecdb36d7d1703e02 - Sigstore transparency entry: 2288174252
- Sigstore integration time:
-
Permalink:
crzidea/mcp-yieldshell@bdcefa4b8515cc19e75d6bf3671afb10dc38d27e -
Branch / Tag:
refs/tags/v1.1.0 - Owner: https://github.com/crzidea
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@bdcefa4b8515cc19e75d6bf3671afb10dc38d27e -
Trigger Event:
push
-
Statement type: