Skip to main content
Glama
by elusznik
ARCHITECTURE.md8.06 kB
# MCP Server Code Execution Mode Architecture ## Overview The bridge runs user-supplied Python inside a **rootless container** and proxies host-managed MCP servers into that sandbox when requested. Historical prototypes with significant security flaws were abandoned rather than archived, serving as lessons for what not to do in secure code execution. ## High-Level Flow ``` ┌───────────────────────────┐ │ MCP Client (e.g. Claude) │ └──────────────┬────────────┘ │ JSON-RPC over stdio ▼ ┌───────────────────────────┐ │ main.py / mcp_server_code_execution_mode │ │ - Exposes run_python tool │ │ - Validates input │ │ - Loads MCP servers │ └──────────────┬────────────┘ │ Async subprocess + JSON stdio bridge ▼ ┌───────────────────────────┐ │ Rootless container runtime│ │ (podman or docker rootless)│ │ - Read-only rootfs │ │ - Tmpfs work dir │ │ - No network │ │ - No Linux capabilities │ └──────────────┬────────────┘ │ ▼ ┌───────────────────────────┐ │ python:3.14-slim image │ │ - Runs async entrypoint │ │ - Executes provided code │ │ - Calls mcp_<alias> tools │ └───────────────────────────┘ ``` Each execution request receives a fresh container and a temporary `/ipc` mount that carries the generated sandbox entrypoint; JSON-framed messages over stdio mediate MCP tool access through the host. ## Components - **`main.py`**: Thin entry point that launches `mcp_server_code_execution_mode.main()` so the module can be registered as an MCP server. - **`mcp_server_code_execution_mode.py`**: Implements the MCP server, persistent MCP client pool, JSON RPC handlers, request validation, and container lifecycle management. - **`PersistentMCPClient`**: Keeps stdio sessions to discovered MCP servers alive across invocations, avoiding repeated cold starts and shutting down cleanly when the bridge stops. - **`SandboxInvocation`**: Builds per-execution metadata, writes the generated entrypoint into `/ipc`, injects `MCP_AVAILABLE_SERVERS`, and services JSON-RPC requests from the sandbox via `handle_rpc`. - **Runtime helpers**: The generated entrypoint exposes `mcp.runtime` helpers (`discovered_servers`, `list_servers`, `list_servers_sync`, `list_tools`, `list_tools_sync`, `query_tool_docs`, `query_tool_docs_sync`, `search_tool_docs`, `search_tool_docs_sync`, `capability_summary`, `describe_server`, `list_loaded_server_metadata`) so sandboxed code can enumerate options before loading additional tools and answer high-level capability questions without exploratory code. - **Container runtime**: Either `podman` or rootless `docker` must be available in the user namespace. Runtime discovery prefers the value from the `MCP_BRIDGE_RUNTIME` environment variable. - **Podman machine management**: When using Podman, the bridge automatically starts the Podman machine if not running and shuts it down after `MCP_BRIDGE_RUNTIME_IDLE_TIMEOUT` seconds of inactivity (default 300s/5min). - **Historical context**: The project evolved through failed security experiments to the current robust architecture; see `HISTORY.md` for the evolution story. ## Request Lifecycle 1. MCP client invokes the `run_python` tool with code, optional timeout, and an optional list of MCP server names. 2. The bridge discovers server definitions (Claude Code config files and `~/.config/mcp/servers/*.json`, with legacy Claude Desktop paths as fallbacks) and boots persistent MCP clients for the requested servers. 3. `SandboxInvocation` creates a temporary `/ipc` directory, writes the generated `entrypoint.py`, ensures the runtime can mount it, and exports `MCP_AVAILABLE_SERVERS` with serialized tool metadata. 4. The container launches `python -u /ipc/entrypoint.py`; the entrypoint rewires stdio into JSON frames, installs `mcp_servers`/`mcp_<alias>` proxies, and uses an asyncio reader on stdin to receive host RPC responses while supporting top-level `await`. 5. The container executes the user code with network disabled, read-only filesystem, tmpfs workspace, dropped capabilities, and user `65534:65534`. Memory, PID, and CPU limits are derived from environment variables, and `--no-new-privileges` is enforced. 6. The bridge consumes JSON frames from stdout/stderr, routes MCP requests via `SandboxInvocation.handle_rpc`, enforces the timeout, and returns a `CallToolResult` where `structuredContent` carries the full status/IO payload (with empty strings/collections dropped). `content[0].text` is rendered as compact plain text by default, or as a TOON block when `MCP_BRIDGE_OUTPUT_MODE=toon`. The sandbox helper summary embedded in the tool schema only advertises these discovery functions; it never serialises individual server or tool metadata. As a result, the initial system prompt is effectively constant in size and the agent fetches detailed documentation on demand via `discovered_servers()`, `query_tool_docs()`, or `search_tool_docs()`. 7. Temporary IPC assets are cleaned up and MCP clients remain warm for future calls. ## Configuration Environment variables allow most execution parameters to be tuned without code changes: | Variable | Purpose | Default | |----------|---------|---------| | `MCP_BRIDGE_RUNTIME` | Force container runtime (`podman` or `docker`) | auto-detect | | `MCP_BRIDGE_IMAGE` | Container image to run | `python:3.14-slim` | | `MCP_BRIDGE_TIMEOUT` | Default timeout (seconds) | 30 | | `MCP_BRIDGE_MAX_TIMEOUT` | Hard timeout ceiling | 120 | | `MCP_BRIDGE_MEMORY` | Memory limit passed to `--memory` | 512m | | `MCP_BRIDGE_PIDS` | PID limit for `--pids-limit` | 128 | | `MCP_BRIDGE_CPUS` | CPU quota for `--cpus` | host default | | `MCP_BRIDGE_CONTAINER_USER` | UID:GID inside container | 65534:65534 | | `MCP_BRIDGE_RUNTIME_IDLE_TIMEOUT` | Auto-shutdown delay for Podman machine (seconds) | 300 | ## Security Posture - **Rootless execution**: The container runtime operates entirely within the invoking user namespace; no privileged helpers are required. - **Ephemeral filesystem**: Only tmpfs mounts are writable, preventing the code from persisting data on the host. - **Capability drop**: With no capabilities and no-new-privileges set, the process cannot escalate privileges inside the container. - **Network disabled**: Requests cannot reach external services or internal hosts. - **Resource limits**: Memory, PID, and CPU caps prevent abusive resource use. - **MCP mediation**: All MCP traffic traverses the host RPC server, keeping audit visibility and allowing future policy enforcement. ## Current Limitations - Automated end-to-end tests for the container layer are not bundled because they would require a runtime and image download during test runs. - Observability is limited to basic logging; structured events and metrics remain future work. - Runtime discovery stops at the first available binary; richer diagnostics and configurability are desired. ## Extensibility Notes - Additional policy checks (e.g. allowlists/denylists) can be added inside `SandboxRPCServer._dispatch` before forwarding MCP calls. - Alternative images can be supplied through `MCP_BRIDGE_IMAGE` provided they contain Python 3 and accept `python -` to execute code from stdin. - Additional hardening (e.g. seccomp profiles or AppArmor) can be layered by injecting runtime-specific arguments once the chosen container runtime is known.

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/elusznik/mcp-server-code-execution-mode'

If you have feedback or need assistance with the MCP directory API, please join our Discord server