LLM provider starters
Copy-paste starters for OpenAI, Anthropic, Gemini and Grok. The API key stays on the orchestrator; the sandbox gets a one-time ticket on stdin.
These starters mint a Mountable ticket from an agent orchestrator and mount
that filesystem inside an ephemeral sandbox. They follow
Mount from a sandbox and the agent workflow.
Tested modules live in
examples/llm-providers
in the public CLI repository. They use the Python standard library only.
What crosses the boundary
MOUNTABLE_API_KEY stays in the orchestrator environment. The mount process
reads the one-time ticket on stdin. In a shell, that looks like this, where
MOUNTABLE_TICKET is the ticket and not the API key:
mkdir -p /mnt/work
printf '%s\n' "$MOUNTABLE_TICKET" | mountable mount --ticket-stdin --json /mnt/work &The & leaves mount running so it can keep renewing the session certificate.
Wait for the JSON line with event mounted before using /mnt/work. The
ticket is single-use and must be exchanged within five minutes. Keep the
session id on the orchestrator and revoke with
mountable sessions revoke SESSION_ID. A revoked mount stops admitting new
operations within fifteen seconds. Retry rules for a lost ticket are on
Agents. Do not mount when the response has no ticket.
When the orchestrator calls prepare, start the child with mount.env,
which already has the API key removed. When it only has the tool result,
use sandbox_env instead of passing os.environ through unchanged. In the
sandbox, run mkdir -p /mnt/work before starting sandbox.argv, write
sandbox.stdin to that child's stdin, and leave the process running. Do not
wait for it to exit. A remote sandbox does not inherit the orchestrator
environment; send it sandbox.argv and sandbox.stdin only.
Shared handoff
On the orchestrator, with MOUNTABLE_API_KEY set:
from handoff import prepare
mount = prepare(filesystem_id, idempotency_key, read_only=False)
# In the sandbox, run mkdir -p /mnt/work before starting mount.argv.
# Start that child and leave it running. Write mount.stdin to its stdin,
# using mount.env so the API key is absent. Read stdout until the mounted
# event. Do not wait for the process to exit.prepare runs mountable ticket create FS_ID --idempotency-key KEY --json
(and --ro when read_only is true). The API key is an environment variable
of that process, not a command argument. The same create_mount_session
function is what each provider tool calls. Its return value is the tool
result: session_id for a later revoke, and sandbox for the mount.
OpenAI Agents SDK and Responses API
examples/llm-providers/openai_responses.py exports TOOL and
create_mount_session.
Agents SDK, in the orchestrator process:
from agents import Agent, function_tool
from openai_responses import create_mount_session
agent = Agent(
name="orchestrator",
instructions="Mint a Mountable ticket before starting a sandbox. Pass only sandbox.stdin into the sandbox.",
tools=[function_tool(create_mount_session)],
)Responses API: send TOOL in tools. TOOL is a strict function named
create_mount_session with filesystem_id, idempotency_key and
read_only. On a function_call, run create_mount_session on the
orchestrator and return its JSON. Start the sandbox from sandbox, not from
the orchestrator's environment.
Anthropic Claude Code and tool use
Claude Code on the trusted machine should use the MCP server.
MOUNTABLE_API_KEY belongs in the MCP server environment. Call
create_mount_ticket. The result includes ticket and mount_command.
Pass the ticket to the sandbox on stdin. Do not copy the MCP env block
into the sandbox.
For the Messages API, examples/llm-providers/anthropic_tools.py exports
TOOL (name, description, input_schema) and the same
create_mount_session function. Run that function on the orchestrator when
the model calls the tool.
Gemini ADK and function calling
examples/llm-providers/gemini_adk.py exports create_mount_session. ADK
turns that function into a tool:
from google.adk.agents.llm_agent import Agent
from gemini_adk import create_mount_session
root_agent = Agent(
name="orchestrator",
model="gemini-flash-latest",
instruction="Mint a Mountable ticket before starting a sandbox. Pass only sandbox.stdin into the sandbox.",
tools=[create_mount_session],
)A researcher agent and a coder agent can share one filesystem: mint rw for
the writer and read_only=True for the reader. Raw function calling uses
FUNCTION_DECLARATION in the same file. The function still runs on the
orchestrator.
xAI Grok
examples/llm-providers/xai_grok.py uses the same flat Responses tool as
OpenAI. Point the client at xAI and handle the call with
create_mount_session on the orchestrator:
import os
from openai import OpenAI
from xai_grok import TOOL
client = OpenAI(base_url="https://api.x.ai/v1", api_key=os.environ["XAI_API_KEY"])
# client.responses.create(model="grok-4.7", input=messages, tools=[TOOL])XAI_API_KEY is the Grok credential. It is separate from MOUNTABLE_API_KEY,
and neither one goes into the sandbox. The xAI SDK's xai_sdk.chat.tool
accepts the same name, description and parameters.
After the mount
Wait for the JSON line with event mounted before reading or writing
/mnt/work. Unmount with mountable unmount /mnt/work so pending writes
finish. The sandbox needs FUSE (/dev/fuse) and the mountable CLI; see
Mount from a sandbox.