Python Execution Service#
The Python Execution Service runs user-provided code in an isolated
environment with safety checks, process isolation, and timeout enforcement.
Claude uses it via the execute MCP tool to perform data analysis,
plotting, and control-system interactions on behalf of the operator.
What It Does#
The service accepts Python source code, applies layered safety checks, and
runs it in either a container (Jupyter kernel over WebSocket) or a
local subprocess (ExecutionWrapper). Results—stdout, stderr,
figures, and saved artifacts—are returned as structured JSON.
Claude → execute MCP tool → safety checks → container / subprocess → result JSON
All packages installed in the deployment environment are available to
executed code (numpy, pandas, scipy, matplotlib, plotly, etc.).
A save_artifact(obj, title="Untitled", description="", artifact_type=None)
helper is injected into the subprocess namespace for saving objects to the
artifact gallery. The artifact_type parameter overrides automatic type
detection (e.g., "figure", "dataframe").
MCP Tool Interface#
The server exposes a single tool, execute, registered on the python
FastMCP server (osprey.mcp_server.python_executor).
Parameter |
Default |
Description |
|---|---|---|
|
(required) |
Python source code to run. |
|
(required) |
Human-readable description of what the code does. |
|
|
|
|
|
Save code and output to a workspace data file and artifact store. |
The tool returns a JSON object containing:
output / error — stdout and stderr truncated to 500 characters. Full output is available in the saved data file.
artifact_ids — IDs for any figures or saved objects.
notebook_artifact_id — ID of an auto-generated notebook capturing the run.
gallery_url — link to the artifact gallery (when available).
has_errors, status (
"Success"/"Failed"), and detected_patterns for control-system operation metadata.
Execution Modes#
Container Execution#
The default mode. Code is sent to a Jupyter kernel running inside a Docker
container via WebSocket. Separate containers can be configured for
readonly and readwrite modes:
# config.yml
osprey:
execution:
execution_method: container
services:
jupyter:
containers:
read:
hostname: localhost
port_host: 8088
write:
hostname: localhost
port_host: 8089
Container mode provides the strongest isolation—the MCP server process is never exposed to user code.
Local Subprocess Execution#
When containers are unavailable, the service falls back to local subprocess
execution. The ExecutionWrapper wraps user code with safety
monkeypatches (e.g., epics.caput() validation against the limits
database), writes the wrapped script to an execution folder, and runs it
as a subprocess:
osprey:
execution:
execution_method: local
python_env_path: /path/to/venv # optional; defaults to sys.executable
The subprocess working directory is set to the project root so that
relative workspace paths (e.g. _agent_data/data/002_archiver_read.json)
resolve correctly.
Security Model#
Five safety layers are applied in sequence:
Static safety check (
quick_safety_check)—blocks dangerous patterns such as dynamic code evaluation, dynamic imports, andsubprocesscalls before execution begins.Control-system pattern detection (
detect_control_system_operations)—identifies read and write patterns. Inreadonlymode, detected writes cause immediate rejection.Limits monkeypatch (
ExecutionWrapper/LimitsValidator)—at runtime,epics.caput()calls are intercepted and validated against the channel limits database. Out-of-range values are blocked.Process isolation—code always runs outside the MCP server process, either in a container or a local subprocess.
Execution timeout—configurable via
python_executor.execution_timeout_seconds(default 600 s). The process is killed if it exceeds the limit.
osprey:
python_executor:
execution_timeout_seconds: 300
Control system operations in user code
Python code interacts with control systems using
osprey.runtime utilities (read_channel(), write_channel()),
not direct connector imports. The execution wrapper configures these
automatically from the deployment context, so code works with any
connector (EPICS, Mock, etc.) and notebooks remain reproducible.
Note
There is no in-framework code-generation pipeline. Claude Code generates
Python code itself and invokes the execute MCP tool directly.
Note
Write approval is handled by the execution_mode parameter. Claude Code
requests user confirmation before calling execute with
execution_mode="readwrite"—there is no separate approval API.
Installation#
The Python executor is included in the default Osprey installation:
uv sync
No additional setup is needed for local subprocess mode. For container
mode, configure the Jupyter container endpoints in config.yml as shown
above.
See Also#
MCP Servers for how the
pythonserver fits into the overall system.src/osprey/mcp_server/python_executor/for the full server source.src/osprey/services/python_executor/for execution engine internals, safety checks, and pattern detection.