Skip to main content

Why Use Governed Runtime

Falling back to unrestricted shell access bypasses SDL-MCP’s policy layer. sdl.runtime.execute keeps command execution consistent with SDL policy:
  • Executable allowlisting prevents unauthorized binaries from running
  • CWD jailing confines execution to within the repo root
  • Environment scrubbing passes only whitelisted environment variables to subprocesses
  • Timeout enforcement prevents runaway processes
  • Concurrency limits prevent resource exhaustion
  • Full audit logging of every execution
In Code Mode, agents should call runtime execution through runtimeExecute inside sdl.chain.
Runtime execution is disabled by default. You must explicitly set runtime.enabled: true in your SDL-MCP configuration file before sdl.runtime.execute will accept requests.

Supported Runtimes

16 runtimes are supported across Windows, Linux, and macOS.
RuntimeTypical ExecutableCommon Uses
nodenode or bunJavaScript tests, scripts, build tooling
typescripttsx / ts-nodeTypeScript scripts without pre-compilation
pythonpython3 / pythonTests, scripts, analysis, automation
shellbash / sh / cmd.exe / powershellGeneral command execution
rubyrubyRuby scripts and tests
phpphpPHP scripts
perlperlPerl scripts
rRscriptR scripts and analysis
elixirelixirElixir scripts

Code Mode vs Args Mode

sdl.runtime.execute supports two invocation styles:
  • Args mode — pass the runtime and an args array. SDL-MCP resolves the executable and spawns the process with those arguments.
  • Code mode — pass source code directly. SDL-MCP writes it to a temp file, compiles it if needed, and executes it.
// Args mode — run an existing test file
{
  "repoId": "my-repo",
  "runtime": "node",
  "args": ["--test", "tests/auth.test.ts"],
  "outputMode": "minimal",
  "timeoutMs": 30000
}

The outputMode Parameter

The outputMode parameter controls how much output is returned in the response. This is the primary lever for controlling token consumption.
ModeDefaultEst. TokensWhat You Get
"minimal"Yes~50{status, exitCode, signal, durationMs, outputLines, outputBytes, artifactHandle} — no stdout/stderr content
"summary"No~200–500Head + tail output excerpts (legacy behavior)
"intent"NoVariableOnly lines matching queryTerms — no head/tail summary
Choosing a mode:
  • Use minimal (the default) when you only need to know whether a command succeeded. Follow up with sdl.runtime.queryOutput to search the persisted artifact if the command failed.
  • Use summary to restore legacy behavior where head + tail excerpts are returned inline. Useful for short commands where you always want to see some output.
  • Use intent when you provide queryTerms and only care about matching lines. No head/tail summary is included — only matched excerpts.
All modes enforce a 500-character per-line cap. Lines exceeding this are truncated with a [truncated] suffix, preventing a single long line (such as minified JSON) from consuming the entire response budget.

Two-Phase Pattern: Execute + Query

The recommended workflow for most runtime tasks:
1

Execute with minimal output

Run the command and receive a lightweight status response with an artifactHandle.
{
  "repoId": "my-repo",
  "runtime": "node",
  "args": ["--test", "tests/auth.test.ts"],
  "outputMode": "minimal",
  "timeoutMs": 30000
}
Response (~50 tokens):
{
  "status": "failure",
  "exitCode": 1,
  "signal": null,
  "durationMs": 4200,
  "outputLines": 312,
  "outputBytes": 18400,
  "artifactHandle": "runtime-my-repo-1774356909696-fc5aa1f22e33e17c"
}
2

Check the exit code

If exitCode is 0, the command succeeded. Move on without reading any output — zero tokens spent on content you don’t need.
3

Query the artifact on failure

If the command failed, use sdl.runtime.queryOutput to search the persisted artifact for specific error terms.
{
  "artifactHandle": "runtime-my-repo-1774356909696-fc5aa1f22e33e17c",
  "queryTerms": ["FAIL", "Error", "AssertionError"],
  "maxExcerpts": 5,
  "contextLines": 3
}
Response:
{
  "artifactHandle": "runtime-my-repo-1774356909696-fc5aa1f22e33e17c",
  "excerpts": [
    {
      "lineStart": 45,
      "lineEnd": 51,
      "content": "  45| not ok 3 - authenticate() rejects expired tokens\n  46|   ---\n  47|   Error: AssertionError: expected 401 but got 200\n  ...",
      "source": "stdout"
    }
  ],
  "totalLines": 312,
  "totalBytes": 18400,
  "searchedStreams": ["stdout", "stderr"]
}
This pattern minimizes tokens in the common case (command succeeded, move on) while still providing full output access when needed.

sdl.runtime.queryOutput

Retrieves and searches stored runtime output artifacts on demand. Use this after an outputMode: "minimal" execution to inspect specific parts of the output without loading it all into context.
ParameterTypeRequiredDescription
artifactHandlestringYesHandle returned by sdl.runtime.execute
queryTermsstring[]YesKeywords to search for in the output
maxExcerptsintegerNoMaximum excerpt windows to return (default: 10)
contextLinesintegerNoLines of context around each match (default: 3)
streamstringNo"stdout", "stderr", or "both" (default: "both")

Security Model

Every runtime request passes through SDL-MCP governance in order:
  1. Feature gateruntime.enabled must be true
  2. Allowed runtime check — runtime must be in allowedRuntimes
  3. Executable validation — executable must be compatible with the chosen runtime
  4. CWD jailing — working directory must be within the repo root
  5. Environment scrubbing — only PATH and explicitly allowlisted env vars are passed to the subprocess
  6. Timeout and output caps — hard limits on duration, stdout bytes, and stderr bytes
  7. Concurrency limits — maximum concurrent jobs enforced

Configuration

{
  "runtime": {
    "enabled": true,
    // Default allowlist: ["node", "python"]. Add more from the 16 supported runtimes.
    "allowedRuntimes": ["node", "python", "shell"],
    "maxDurationMs": 600000,
    "maxConcurrentJobs": 2,
    "maxStdoutBytes": 1048576,
    "maxStderrBytes": 262144,
    "maxArtifactBytes": 10485760,
    "artifactTtlHours": 24,
    // Allowlist additional executables beyond the runtime defaults
    "allowedExecutables": [],
    // Environment variables passed through to subprocesses
    "envAllowlist": ["NODE_ENV", "DATABASE_URL"]
  }
}
For enforced agent setups, this runtime block is generated automatically:
sdl-mcp init --client <client> --enforce-agent-tools

Real Examples

{
  "repoId": "my-repo",
  "runtime": "node",
  "args": ["--test", "tests/auth.test.ts"],
  "outputMode": "minimal",
  "timeoutMs": 30000
}

SDL-First Guidance

When SDL-MCP is configured for agent enforcement:
  • Prefer runtimeExecute inside sdl.chain over native shell tools
  • Prefer the two-phase pattern: outputMode: "minimal" then sdl.runtime.queryOutput on demand
  • Prefer structured query terms over dumping large output back to the model
  • Use shell only when a shell is genuinely necessary, not as a default catch-all runtime

Build docs developers (and LLMs) love