MCP Server Design
Note: The
abxcommands shown on this page are provided by the Node.js CLI in theclirepository (npm install -g @agent-sandbox/cli). CLI-specific documentation (CLI design, tutorial, reference) lives there too; this repository documents the Python SDK (agent-sandbox) and the container-side runtime (agent-sandbox-server).Agent Sandbox MCP Server exposes sandbox capabilities as MCP (Model Context Protocol) Tools, allowing AI Agents (Cursor, Claude Desktop, VS Code, etc.) to directly operate cloud sandboxes.
Implemented surface (kept in sync with client-sdk/src/agent_sandbox/agent/):
- 7 P0 tools (
agent/tools.py) - STDIO transport (
agent/mcp.py) —python -m agent_sandbox.agent.mcp - Streamable HTTP transport (
agent/mcp_http.py) — Starlette ASGI app - The
abx mcp install / start / status / deploycommands are provided by the Node.js CLI (see Related: the abx CLI)
1. Tools Definition
All 7 tools are defined in TOOL_SCHEMAS (agent/tools.py). Omitting sandbox_id targets the default sandbox (see Section 2).
create_sandbox
{
"name": "create_sandbox",
"description": "Create a cloud sandbox and make it the session default. When template is omitted, use the MCP server's configured template (code-interpreter-v1 for abx mcp start).",
"inputSchema": {
"type": "object",
"properties": {
"template": {
"type": "string",
"description": "Sandbox template name; omitted means the server template"
},
"timeout": {
"type": "integer",
"description": "Sandbox timeout in seconds, default 300",
"default": 300
},
"envs": {
"type": "object",
"description": "Environment variable key-value pairs",
"additionalProperties": { "type": "string" }
}
}
},
"returns": {
"sandbox_id": "string — Sandbox ID",
"status": "string — Sandbox status",
"url": "string — Sandbox access URL"
}
}
run_code
{
"name": "run_code",
"description": "Execute code in the sandbox (via the code interpreter). Supports Python, JavaScript, and more. Uses the default sandbox when sandbox_id is omitted.",
"inputSchema": {
"type": "object",
"properties": {
"code": { "type": "string", "description": "Code to execute" },
"language": {
"type": "string",
"description": "Programming language, default python",
"enum": ["python", "javascript", "shell", "typescript", "r"],
"default": "python"
},
"sandbox_id": {
"type": "string",
"description": "Sandbox ID; uses the default sandbox when omitted"
},
"timeout": {
"type": "integer",
"description": "Execution timeout in seconds, default 30",
"default": 30
}
},
"required": ["code"]
},
"returns": {
"stdout": "string",
"stderr": "string",
"exit_code": "integer",
"output_files": "array — [{name, path, size}] generated files"
}
}
run_command
{
"name": "run_command",
"description": "Execute a shell command in the sandbox. Uses the default sandbox when sandbox_id is omitted.",
"inputSchema": {
"type": "object",
"properties": {
"command": { "type": "string", "description": "Shell command" },
"sandbox_id": { "type": "string", "description": "Sandbox ID" },
"cwd": {
"type": "string",
"description": "Working directory; omitted uses the image workdir",
"default": "/app"
},
"timeout": {
"type": "integer",
"description": "Execution timeout in seconds, default 60",
"default": 60
}
},
"required": ["command"]
},
"returns": {
"stdout": "string",
"stderr": "string",
"exit_code": "integer"
}
}
read_file
{
"name": "read_file",
"description": "Read file content from the sandbox. Uses the default sandbox when sandbox_id is omitted.",
"inputSchema": {
"type": "object",
"properties": {
"path": { "type": "string", "description": "Absolute file path" },
"sandbox_id": { "type": "string", "description": "Sandbox ID" },
"encoding": {
"type": "string",
"description": "File encoding, default utf-8",
"default": "utf-8"
}
},
"required": ["path"]
},
"returns": { "content": "string" }
}
write_file
{
"name": "write_file",
"description": "Create or overwrite a file in the sandbox. Uses the default sandbox when sandbox_id is omitted.",
"inputSchema": {
"type": "object",
"properties": {
"path": { "type": "string", "description": "Absolute file path" },
"content": { "type": "string", "description": "File content" },
"sandbox_id": { "type": "string", "description": "Sandbox ID" }
},
"required": ["path", "content"]
},
"returns": { "success": "boolean", "bytes_written": "integer" }
}
list_files
{
"name": "list_files",
"description": "List files and subdirectories in a sandbox directory. Uses the default sandbox when sandbox_id is omitted.",
"inputSchema": {
"type": "object",
"properties": {
"path": {
"type": "string",
"description": "Directory path, default /",
"default": "/app"
},
"sandbox_id": { "type": "string", "description": "Sandbox ID" }
}
},
"returns": {
"files": "array — [{name, path, type, size}]"
}
}
kill_sandbox
{
"name": "kill_sandbox",
"description": "Destroy the specified sandbox, or the default sandbox when sandbox_id is omitted.",
"inputSchema": {
"type": "object",
"properties": {
"sandbox_id": {
"type": "string",
"description": "Sandbox ID to destroy; destroys the default sandbox when omitted"
}
}
},
"returns": { "success": "boolean" }
}
2. Session Binding Design
Default Sandbox Concept
The MCP Server introduces a "default sandbox" concept to simplify Agent operations:
graph TD
A["Agent's first tool call (no sandbox_id)"] --> B[MCP Server automatically creates a default sandbox]
B --> C["Subsequent calls without sandbox_id reuse the default sandbox"]
Behavior rules (implemented by SandboxManager in agent/mcp.py):
create_sandboxcreates a sandbox and makes it the default. Later calls that omitsandbox_iduse that sandbox. Earlier sandboxes stay alive untilkill_sandboxor the session ends- When there is no default yet, the first tool call that omits
sandbox_idlazily creates one. Concurrent lazy creates share a single sandbox - Default template:
code-interpreter-v1when started viaabx mcp start(the CLI default);basewhen constructingSandboxMCPServer/ the HTTPSessionStoreprogrammatically without an explicit template.create_sandboxuses that server template whentemplateis omitted kill_sandboxwithoutsandbox_iddestroys the default sandbox, and returns an error when there is no default. Session end destroys every sandbox in the session — STDIO EOF / server shutdown, or HTTPDELETE /mcp/ idle session TTL expiry- An explicit
sandbox_idaddresses that sandbox and does not change the default. Each use callsSandbox.set_timeoutwith the timeout from create, extending the lifetime
sequenceDiagram
participant Agent
participant MCP as MCP Server
participant SB1 as sb-001
participant SB2 as sb-002
Note over Agent,MCP: Session starts
Agent->>MCP: run_code("print(1)")
MCP->>SB1: Auto-create default sandbox sb-001
Agent->>MCP: run_code("print(2)")
MCP->>SB1: Reuse sb-001
Agent->>MCP: create_sandbox(template=...)
MCP->>SB2: Create sb-002 and make it the default
Agent->>MCP: write_file / run_code (no sandbox_id)
MCP->>SB2: Use sb-002
Agent->>MCP: run_code("...", sandbox_id=sb-001)
MCP->>SB1: Explicit id still addresses sb-001
Note over Agent,MCP: Session ends
MCP->>SB1: Destroy sb-001 and sb-002
3. Transport Methods
STDIO — Local IDE Integration
graph LR
IDE["IDE / Agent<br/>Cursor, Claude, VS Code"] <-->|"STDIO stdin/stdout<br/>newline-delimited JSON-RPC 2.0"| MCP["MCP Server<br/>python -m agent_sandbox.agent.mcp"]
MCP --> SM[SandboxManager]
SM --> FC[Alibaba Cloud FC]
- Implementation:
agent/mcp.py— a self-contained minimal JSON-RPC 2.0 handler over newline-delimited STDIO; no externalmcpSDK dependency required - Protocol version:
2024-11-05— the only version the STDIO server supports.initializeechoes it when requested and falls back to it for missing/unsupported requests, preserving the original behavior - Supported methods:
initialize,notifications/initialized,tools/list,tools/call,ping - Use case: local development, single user; the IDE spawns the process
- Startup:
python -m agent_sandbox.agent.mcp(typically launched by the IDE), configured fromABX_SANDBOX_API_KEY/ABX_SANDBOX_BASE_URL/ABX_SANDBOX_DOMAIN/ABX_DEFAULT_TEMPLATE(see Environment Variables below). The CLI server isabx mcp start [--template NAME] [--sandbox-api-key KEY] [--sandbox-base-url URL] [--sandbox-domain DOMAIN], withabx mcp stopand--http --background
Streamable HTTP — Remote Deployment
graph LR
A["Client A - Cursor"] <-->|"Streamable HTTP<br/>POST /mcp"| MCP["MCP Server<br/>FC function / uvicorn"]
B["Client B - Claude"] <-->|"Streamable HTTP<br/>POST /mcp"| MCP
MCP --> SM["SessionStore<br/>routes by Mcp-Session-Id"]
SM --> FC[Alibaba Cloud FC]
- Implementation:
agent/mcp_http.py— Starlette ASGI application returned bycreate_mcp_app(); also exposed as the module-levelasgi_appentry point - Protocol: MCP Streamable HTTP (spec 2025-06-18)
- Endpoints:
POST /mcp— JSON-RPC requests;initializecreates a session and returnsMcp-Session-Id; subsequent requests must carry the headerDELETE /mcp— session termination and sandbox cleanup (requiresMcp-Session-Id)GET /mcp— returns 405; SSE server-initiated notifications are not implementedGET /health— health probe returning{"status": "ok", "protocol": "2025-06-18"}- Sessions: in-process
SessionStore; idle TTL 3600 s (default), max 100 concurrent sessions (default); capacity exhaustion returns 503 with JSON-RPC error-32000 - Version negotiation:
initializeechoes the client-requestedprotocolVersionwhen supported; this transport supports2025-06-18only, matchingGET /health. Missing or unsupported requested versions negotiate to2025-06-18— per the MCP spec the server responds with a version it supports, and clients that cannot accept it may disconnect. The STDIO transport independently keeps2024-11-05 - Use case: remote service, team sharing, multiple clients; deploy to Alibaba Cloud FC via the artifact generated by the CLI's
abx mcp deploy, or run locally with uvicorn - Optional dependency: Starlette + uvicorn (
pip install 'agent-sandbox[mcp]')
Authentication
- Client → MCP:
Authorization: Bearer <token>, validated with constant-time comparison; configured viaABX_MCP_AUTH_TOKEN. On loopback, leaving it unset disables authentication; a configured-but-empty token fails closed (every request gets 401). The FC artifact callsrequire_auth_tokenand refuses to start when the token is empty - MCP → Sandbox:
ABX_SANDBOX_API_KEYenvironment variable
Environment Variables
| Variable | Purpose |
|---|---|
ABX_SANDBOX_API_KEY |
Sandbox backend API key |
ABX_SANDBOX_BASE_URL |
Platform API URL |
ABX_SANDBOX_DOMAIN |
Sandbox domain |
ABX_DEFAULT_TEMPLATE |
Default sandbox template |
ABX_MCP_AUTH_TOKEN |
Bearer token for client authentication (HTTP transport) |
4. Server Architecture
graph TD
subgraph MCP["MCP Server"]
TL["Transport Layer\nSTDIO (mcp.py) / Streamable HTTP (mcp_http.py)"]
TR["Tool Registry\n7 P0 tools (agent/tools.py)"]
SM["SandboxManager / SessionStore"]
RR["JSON-RPC Router\ninitialize / tools/list / tools/call / ping"]
SC["Sandbox Client\nagent_sandbox SDK"]
end
TL --> RR
TR --> RR
SM --> RR
RR --> SC
SC --> FC["Alibaba Cloud FC Sandbox Runtime"]
5. Installation and CLI
The Python server is registered in an IDE like any STDIO MCP server. The entry is python -m agent_sandbox.agent.mcp; the IDE passes the credentials in env. Examples are in Section 6.
The abx mcp install --target cursor|claude|vscode (merges an agent-sandbox entry into the IDE config), abx mcp status (server name, transport, tool list, whether an API key is configured, per-IDE install flags), abx mcp start|stop and abx mcp deploy commands belong to the Node.js CLI; their design is the CLI's MCP Server design.
6. Configuration Examples
Claude Desktop
// ~/Library/Application Support/Claude/claude_desktop_config.json
{
"mcpServers": {
"agent-sandbox": {
"command": "python",
"args": ["-m", "agent_sandbox.agent.mcp"],
"env": {
"ABX_SANDBOX_API_KEY": "your-api-key"
}
}
}
}
Cursor
// ~/.cursor/mcp.json
{
"mcpServers": {
"agent-sandbox": {
"command": "python",
"args": ["-m", "agent_sandbox.agent.mcp"],
"env": {
"ABX_SANDBOX_API_KEY": "your-api-key"
}
}
}
}
VS Code
// .vscode/mcp.json
{
"servers": {
"agent-sandbox": {
"type": "stdio",
"command": "/path/to/python",
"args": ["-m", "agent_sandbox.agent.mcp"]
}
}
}
The CLI's abx mcp install writes the equivalent entries with command set to abx and args set to ["mcp", "start"].
Remote (Streamable HTTP)
After deploying the HTTP app (Section 7), point clients at the endpoint; add a Bearer header when a token is configured:
{
"mcpServers": {
"agent-sandbox-remote": {
"url": "https://<FC_HTTP_TRIGGER_URL>/mcp",
"headers": {
"Authorization": "Bearer <BEARER_TOKEN>"
}
}
}
}
7. FC Deployment
Status:
abx mcp deploygenerates a deployment artifact; automatic FC API deployment is not implemented. Manual deployment steps are printed after generation.
Architecture
The MCP Server can be deployed to Alibaba Cloud Function Compute (FC) as a Streamable HTTP endpoint, leveraging FC's MCP session affinity routing.
graph TB
CLI["abx mcp deploy → artifact directory"]
FC["FC function: agent-sandbox-mcp (created manually)"]
Sandbox["Envd sandbox (separate FC instance)"]
CLI --> FC
FC --> Sandbox
subgraph FC_Function ["FC function"]
ASGI["app.py → agent_sandbox.agent.mcp_http:asgi_app"]
Trigger["HTTP trigger: POST/GET/DELETE /mcp"]
Session["Session affinity: Mcp-Session-Id"]
Env["Env vars: ABX_SANDBOX_API_KEY / ABX_MCP_AUTH_TOKEN / ABX_DEFAULT_TEMPLATE"]
end
Deployment Command
The artifact is generated by abx mcp deploy (options, region resolution, token sources); see the CLI's MCP Server design and CLI Design — mcp deploy. The sections below describe the artifact and the application it runs, which this SDK provides.
Artifact Contents
| File | Content |
|---|---|
requirements.txt |
agent-sandbox[mcp], uvicorn>=0.29 |
app.py |
ASGI entry point reading ABX_MCP_AUTH_TOKEN, ABX_SANDBOX_API_KEY, ABX_SANDBOX_BASE_URL, ABX_SANDBOX_DOMAIN, ABX_DEFAULT_TEMPLATE from the FC function environment |
config.yaml |
YAML deployment manifest (not an FC API payload): function_name, region, runtime (python3.10), handler (app.app), memory, timeout, environment_variables, http_trigger (methods POST/GET/DELETE + enable_session_affinity), optional custom_domain |
abx mcp deploy also prints manual deployment steps (package the artifact, create the function via the FC console or SDK, create an HTTP trigger, enable Mcp-Session-Id affinity if supported) and an IDE config template with the Bearer token masked.
Key Design Points
- Protocol: Streamable HTTP (MCP spec 2025-06-18);
initializenegotiates2025-06-18consistently withGET /health(STDIO keeps2024-11-05) - Session affinity: delegated to the FC platform layer via the
Mcp-Session-Idheader — no application-level sticky routing needed - Authentication: dual-layer — client→MCP uses
Authorization: Bearer <token>; MCP→sandbox usesABX_SANDBOX_API_KEYfrom the FC env - Lifecycle: sandboxes are lazily created per MCP session;
DELETE /mcptriggers cleanup; the idle-session TTL is the safety net - Cold start: FC cold start (~1–3 s) may conflict with MCP initialize timeouts; provisioned instances are recommended for production
- Secrets:
config.yamlcontains the API key and Bearer token — never commit it to version control
See the MCP Integration guide for end-to-end usage; the full abx mcp deploy parameter reference is in the CLI repository (see Deployment Command).
Related: the abx CLI
The Node.js abx CLI has its own MCP server implementation and the commands around it: MCP Server design (CLI) (abx mcp install|start|stop|status|deploy, tool schemas as implemented in TypeScript, FC deployment) and CLI Command System.