Home
Why Sandlock
How It Works Use Cases Comparison Security Model
Docs
Documentation Home Getting Started CLI Reference Python SDK Sandbox Reference
Products
Overview Sandbox HTTP API Sandbox Scheduler
GitHub Schedule a Demo
Sandlock / Docs /MCP Server

MCP Server

An MCP server whose tools execute inside a Landlock and seccomp sandbox, one per call. Deny by default: each tool declares the capabilities it needs and gets nothing else. No Docker required.

Install and run

shell
$ cd python && pip install -e '.[mcp]'

# local, stdio transport
$ sandlock-mcp --workspace /tmp/sandbox

# remote, SSE transport
$ sandlock-mcp --transport sse --host 0.0.0.0 --port 8080 \
    --workspace /tmp/sandbox

The mcp-remote extra additionally pulls in uvicorn and starlette for the SSE transport.

FlagDefaultDescription
--workspaceauto-created temp dirThe one directory tools may write to
--transportstdiostdio or sse
--host127.0.0.1Bind address for the SSE transport
--port8080Bind port for the SSE transport

Client configuration

Local (stdio)
{
  "mcpServers": {
    "sandlock": {
      "command": "sandlock-mcp",
      "args": ["--workspace", "/tmp/sandbox"]
    }
  }
}
Remote (SSE)
{
  "mcpServers": {
    "sandlock": {
      "url": "http://remote-host:8080/sse"
    }
  }
}

Built-in tools

Each tool runs in its own sandbox, built fresh for the call, with only the capabilities that tool declares.

ToolArgumentsCapabilities granted
shell command Write access to the workspace. No network. Read-only filesystem otherwise.
python code 256 MB of memory. No filesystem or network access.
read_file path None beyond the read-only baseline. Path is relative to the workspace root.
write_file path, content Write access to the workspace. Creates parent directories.
list_files subdir (optional) None beyond the read-only baseline.

Custom tools

McpSandbox is the mechanism underneath, and it is usable directly. It is deny-by-default: a tool added with no capabilities gets a read-only sandbox with no network, and anything more has to be named.

python
from sandlock.mcp import McpSandbox

mcp = McpSandbox(workspace="/tmp/agent")

mcp.add_tool("read_file", read_fn)                     # read-only
mcp.add_tool("write_file", write_fn,
             capabilities={"fs_writable": [workspace]})   # write to workspace

result = await mcp.call_tool("write_file", {"path": "out.txt", "content": "hi"})

Tools must be top-level functions. Each call runs the tool in a fresh interpreter inside the sandbox, so the function has to be importable by name. Lambdas, methods, and nested or closure functions are rejected with a clear error rather than failing mysteriously at call time.

The worker is executed as a plain script rather than through -m, so it runs with only stdlib imports and never pulls the sandlock package, or its FFI shared library, into the sandbox with it.

Why this matters for agents

A tool-using agent is an arbitrary code execution primitive with a model deciding the arguments. The usual mitigation is to run tools in a container, which is heavy enough that most implementations reuse one, and reuse means one call's side effects are visible to the next.

A Sandlock sandbox costs about 5 ms to start, so a fresh sandbox per call is affordable. Combined with deny-by-default capabilities, the blast radius of a tool call is the capabilities that tool declared, and nothing else.

For agents that also need to reach an API, the same techniques from the CLI apply: an HTTP ACL restricting the agent to one method and path, and credential injection so the key stays in the supervisor and never enters the agent's address space.